Related
I am trying to achieve effect as shown in the picture below:
Im using p5.js library but im not a graphics programmer and thats why its very difficult to achieve this particular graphical effect. I am sharing my code with you and also I followed this tutorial.
Index.html
<html>
<head>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
<script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script>
<script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.sound.min.js"></script>
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<script language="javascript" type="text/javascript" src="particle.js"></script>
</head>
<body>
</body>
</html>
particle.js
function Particle() {
this.pos = createVector(random(width), random(height));
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.maxspeed = 4;
this.h = 0;
this.alphaPower = 20;
this.prevPos = this.pos.copy();
this.update = function() {
this.vel.add(this.acc);
this.vel.limit(this.maxspeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.follow = function(vectors) {
var x = floor(this.pos.x / scl);
var y = floor(this.pos.y / scl);
var index = x + y * cols;
var force = vectors[index];
this.applyForce(force);
}
this.applyForce = function(force) {
this.acc.add(force);
}
this.showBlue = function() {
stroke(0, 76, 153, this.alphaPower);
strokeWeight(1);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.showMaroon = function() {
stroke(165, 0, 68, this.alphaPower); // maroon
strokeWeight(1);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.showYellow = function() {
stroke(237, 188, 0, this.alphaPower);
//stroke(1,2);
strokeWeight(1);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function() {
this.prevPos.x = this.pos.x;
this.prevPos.y = this.pos.y;
}
this.edges = function() {
if (this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if (this.pos.x < 0) {
this.pos.x = width;
this.updatePrev();
}
if (this.pos.y > height) {
this.pos.y = 0;
this.updatePrev();
}
if (this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
sketch.js
var inc = 0.01;
var scl = 10;
var cols, rows;
var zoff = 0;
var fr;
var particles = [];
var particlesMaroon = [];
var particlesBlue = [];
var particlesYellow = [];
var flowfield;
var flowfieldMaroon;
var flowfieldBlue;
var flowfieldYellow;
var gamma_is_high = false;
var beta_is_high = false;
var alpha_is_high = false;
var maroon_data=1000;
var blue_data = 1000;
var yellow_data = 1000;
setInterval(function() {
ChangeLines();
}, 5000);
function dataRequest(){
socket.emit('datarequest', {data: "request"});
}
function ChangeLines(){
maroon_data = random(500, 2000);
blue_data = random(500, 2000);
yellow_data = random(500, 2000);
gamma_is_high = true;
alpha_is_high = true;
beta_is_high = true;
}
function setup() {
slider = createSlider(0.01, 0.1, 0.02,0);
slider.position(10, 10);
slider.style('width', '80px');
ChangeLines();
createCanvas(windowWidth-15, windowHeight-20);
cols = floor(width / scl);
rows = floor(height / scl);
fr = createP('');
flowfield = new Array(cols * rows);
flowfieldMaroon = new Array(cols * rows);
flowfieldBlue = new Array(cols * rows);
flowfieldYellow = new Array(cols * rows);
for (var i = 0; i < 1000; i++) {
particles[i] = new Particle();
particlesMaroon[i] = new Particle();
particlesBlue[i] = new Particle();
particlesYellow[i] = new Particle();
}
background(255);
}
function draw() {
let val = slider.value();
inc = val;
var yoff = 0;
for (var y = 0; y < rows; y++) {
var xoff = 0;
for (var x = 0; x < cols; x++) {
var index = x + y * cols;
var angle = noise(xoff, yoff, zoff) * TWO_PI;
var angleMaroon = noise(xoff, yoff, zoff) * TWO_PI;
var angleBlue = noise(xoff, yoff, zoff) * TWO_PI;
var angleYellow = noise(xoff, yoff, zoff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
var vm = p5.Vector.fromAngle(angleMaroon);
var vb = p5.Vector.fromAngle(angleBlue);
var vy = p5.Vector.fromAngle(angleYellow);
v.setMag(5);
vm.setMag(5);
vb.setMag(5);
vy.setMag(5);
flowfield[index] = v;
flowfieldMaroon[index] = vm;
flowfieldBlue[index] = vb;
flowfieldYellow[index] = vy;
xoff += inc;
}
yoff += inc;
}
zoff += 0.0003;
for (var i = 0; i < particles.length; i++) {
if(gamma_is_high==true){
particlesMaroon[i].follow(flowfieldMaroon);
particlesMaroon[i].update();
particlesMaroon[i].edges();
particlesMaroon[i].showMaroon();
}
if(beta_is_high){
particlesBlue[i].follow(flowfieldBlue);
particlesBlue[i].update();
particlesBlue[i].edges();
particlesBlue[i].showBlue();
}
if(alpha_is_high){
particlesYellow[i].follow(flowfieldYellow);
particlesYellow[i].update();
particlesYellow[i].edges();
particlesYellow[i].showYellow();
}
}
fr.html(floor(frameRate()));
}
The variables maroon_data, blue_dataand yellow_data are data coming from EEG device which will be calculated later but for now its hard coded values for prototype only. Also booleans gamma_is_high, beta_is_high and alpha_is_high are set to true for now. But with this much effort I only manage to achieve this as shown below:
And the actual output is like this:
Kindly help me get this actual output. Any help would be appreciated. Thanks.
Ive sorta done this before. I only had one array of particles and just changed their rgb values by a small random amount. I used an object to store the colour values.
function setup() {
//Do all the normal stuff
var c = {r: 100, g:100, b:100)
}
function draw() {
//All the other stuff
c.r += random(-3,3);
c.g += random(-3,3);
c.b += random(-3,3);
}
And then in the particle:
show() {
fill(c.r,c.g,c.b, whateverAlphaYouWant);
//Do all the rest
}
You can always tweak the values to get whatever colour you want.
I have the following code that i've roughly redone from someone else to help learn javascript. I am getting the following error:
Uncaught TypeError: flock.addBoid is not a function
at setup (boids.js:15)
at boids.js:1
The function in question is called at the beginning in setup() and defined shortly after in the section labeled FLOCK. It is definitely not misspelled and these are the only occurrences of addBoid in the document. I'll include the whole document in case its relevant but the problem should just lie in the beginning.
I am not looking for any advice about my code other than the source and solution to this error, thanks.
setup();
draw();
//////////////////// SETUP ///////////////////////
var flock;
var ctx;
var c;
function setup(){
c = document.getElementById("boidCanvas");
ctx = c.getContext("2d");
flock = new Flock();
for(var i = 0; i < 100; i++){
var b = new Boid(ctx.width / 2, ctx.height / 2);
flock.addBoid(b);
}
}
function draw() {
ctx.fillStyle = "Blue";
ctx.fillRect(0,0,c.width,c.height);
//flock.run()
}
//////////////////// FLOCK ///////////////////////
function Flock(){
this.boids = [];
}
Flock.prototype.run = function(){
for(var i = 0; i < this.boids.length; i++){
this.boids[i].run(this.boids);
}
}
Flock.prototype.addBoid = function(b){
this.boids.push(b);
}
//////////////////// BOID ///////////////////////
function Boid(setx,sety){
this.acceleration = { x:0, y:0 };
this.velocity = { x:Math.floor(Math.random()*3)-1, y:Math.floor(Math.random()*3)-1 };
this.position = { x:setx, y:sety };
this.r = 3;
this.maxSpeed = 3;
this.maxForce = .05;
}
Boid.prototype.run = function(boids){
this.evaluate(boids);
this.update();
this.wrap();
this.render();
}
// force is a vector [x,y]
Boid.prototype.applyForce = function(force){
this.acceleration.x += force[0];
this.acceleration.y += force[1];
}
Boid.prototype.evaluate = function(boids){
var seperate = this.seperate(boids);
var align = this.align(boids);
var cohesion = this.cohesion(boids);
// Arbitrary Weights
seperate *= 1.5;
align *= 1.0;
cohesion *= 1.0;
this.applyForce(seperate);
this.applyForce(align);
this.applyForce(cohesion);
}
Boid.prototype.update = function(){
//update velocity
this.velocity += this.acceleration;
//fix velocity to max speed
var normal = normalize([this.velocity.x, this.velocity.y]);
this.velocity = constantMult(normal, this.maxSpeed);
//update position
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
//reset acceleration;
this.acceleration.x = 0;
this.acceleration.y = 0;
}
// target is a vector [x,y]
Boid.prototype.seek = function(target){
var desired = sub(target, [this.position.x, this.position.y]);
var normal = normalize(desired);
desired = constantMult(normal, this.maxSpeed);
var steer = sub(desired,[this.velocity.x, this.velocity.y])
normal = normalize(steer);
steer[0] = normal[0] * this.maxForce;
steer[1] = normal[1] * this.maxForce;
return steer;
}
Boid.prototype.render = function(){
var triangle = drawTriangle(this.velocity);
for(var i = 0; i < triangle.length; i++){
triangle[i] = constantMult(triangle[i], this.r);
}
for(i = 0; i < triangle.length; i++){
triangle[i] = add(triangle[i], this.position);
}
ctx.beginPath();
ctx.moveTo(triangle[0][0], triangle[0][1]);
for(i = 1; i < triangle.length; i++){
ctx.lineTo(triangle[i][0], triangle[i][1]);
}
ctx.closePath();
ctx.fillStyle = "White";
ctx.fill();
}
Boid.prototype.wrap = function(){
if(this.position.x < -this.r)
this.position.x = c.width + this.r;
else if(this.position.x > c.width + this.r)
this.position.x = -this.r;
if(this.position.y < -this.r)
this.position.y = c.height + this.r;
else if(this.position.y > c.height + this.r)
this.position.y = -this.r;
}
Boid.prototype.seperate = function(boids){
var desiredSeperation = 25.0;
var steer = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var d = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(d < desiredSeperation){
var normalDiff = normalize(difference);
normalDiff = constantMult(normalDiff, 1/d);
steer = add(steer, normalDiff);
count++;
}
}
if(count > 0){
steer = constantMult(steer, 1/count);
steer = normalize(steer);
steer = constantMult(steer, this.maxSpeed);
steer = sub(steer, this.velocity);
steer = normalize(steer);
steer = constantMult(steer, this.maxForce);
}
return steer;
}
Boid.prototype.align = function(boids){
var neighborDistance = 50;
var sum = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var dist = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(dist < neighborDistance){
sum = sum(sum, boids[i].velocity);
count++;
}
}
if(count > 0){
sum = constantMult(sum, 1/count);
sum = normalize(sum);
sum = constantMult(this.maxSpeed);
var steer = sub(sum, this.velocity);
steer = normalize(steer);
steer = constantMult(steer, this.maxForce);
return steer;
}
else
return [0,0];
}
Boid.prototype.cohesion = function(boids){
var neighborDistance = 50;
var sum = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var dist = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(dist < neighborDistance){
sum = add(sum, boids[i].position);
count++;
}
}
if(count > 0){
sum = constantMult(sum, 1/count);
return this.seek(sum);
}
else
return [0,0];
}
//////////////////// HELPERS ///////////////////////
// returns the vector with the same direction as v but with magnitude 1 in the form [x,y]
// v is a vector in the form [x,y]
function normalize(v){
var magnitude = Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2))
var normalX = v[0] / magnitude;
var normalY = v[1] / magnitude;
return [normalX, normalY];
}
function add(a,b){
var x = a[0]+b[0];
var y = a[1]+b[1];
return [x,y];
}
// returns a-b, [ax-bx, ay-by]
function sub(a,b){
var x = a[0]-b[0];
var y = a[1]-b[1];
return [x,y];
}
function mult(a,b){
var x = a[0]*b[0];
var y = a[1]*b[1];
return [x,y];
}
function constantMult(a, n){
for(var i = 0; i < a.length; i++){
a[i] *= n;
}
}
// creates an unscaled issoceles triangle centered at the origin
// returns a list of 3 lists, each containing the coordinates of a vertex, the first being the tip
// ie. [ [x1,y1], [x2,y2], [x3,y3] ]
// heading is a vector describing the direction of the triangle in the form [x,y]
// heading does not need to be normalized
function drawTriangle(heading){
heading = normalize(heading);
var v1 = [1,0];
var v2 = [-1, .5];
var v3 = [-1,-.5];
var thetaX = Math.acos(heading[0]);
var thetaY = Math.asin(heading[1]);
var theta;
if(thetaX >= 0)
theta = (Math.PI / 2) - thetaY;
else
theta = (Math.PI / 2) - thetaX;
function rotate(v){
var xp = (v[0] * Math.cos(theta)) - (v[1] * Math.sin(theta));
var yp = (v[1] * Math.cos(theta)) + (v[0] * Math.sin(theta));
return [xp, yp];
}
v1 = rotate(v1);
v2 = rotate(v2);
v3 = rotate(v3);
return [v1,v2,v3];
}
Move the functions setup(); and draw(); to the end of the JavaScript file. The issue was that the function addBoid() was not hoisted to the top, making it undefined by setup(); and draw();.
You function declaration gets hoisted on the top of block.
function Flock(){
this.boids = [];
}
when you add properties to prototype those properties are not eligible for hoisting, its like accessing variables (let declared cause var is eligible for hoisting) before declaring them.
Flock.prototype.run = function(){
for(var i = 0; i < this.boids.length; i++){
this.boids[i].run(this.boids);
}
}
Flock.prototype.addBoid = function(b){
this.boids.push(b);
}
Add above lines before you call flock.addBoid i.e. move setup and draw call to the end of Javascript
//////////////////// SETUP ///////////////////////
var flock;
var ctx;
var c;
//////////////////// FLOCK ///////////////////////
function Flock(){
this.boids = [];
}
Flock.prototype.run = function(){
for(var i = 0; i < this.boids.length; i++){
this.boids[i].run(this.boids);
}
}
Flock.prototype.addBoid = function(b){
this.boids.push(b);
}
function setup(){
c = document.getElementById("boidCanvas");
ctx = c.getContext("2d");
flock = new Flock();
for(var i = 0; i < 100; i++){
var b = new Boid(ctx.width / 2, ctx.height / 2);
flock.addBoid(b);
}
}
function draw() {
ctx.fillStyle = "Blue";
ctx.fillRect(0,0,c.width,c.height);
//flock.run()
}
//////////////////// BOID ///////////////////////
function Boid(setx,sety){
this.acceleration = { x:0, y:0 };
this.velocity = { x:Math.floor(Math.random()*3)-1, y:Math.floor(Math.random()*3)-1 };
this.position = { x:setx, y:sety };
this.r = 3;
this.maxSpeed = 3;
this.maxForce = .05;
}
Boid.prototype.run = function(boids){
this.evaluate(boids);
this.update();
this.wrap();
this.render();
}
// force is a vector [x,y]
Boid.prototype.applyForce = function(force){
this.acceleration.x += force[0];
this.acceleration.y += force[1];
}
Boid.prototype.evaluate = function(boids){
var seperate = this.seperate(boids);
var align = this.align(boids);
var cohesion = this.cohesion(boids);
// Arbitrary Weights
seperate *= 1.5;
align *= 1.0;
cohesion *= 1.0;
this.applyForce(seperate);
this.applyForce(align);
this.applyForce(cohesion);
}
Boid.prototype.update = function(){
//update velocity
this.velocity += this.acceleration;
//fix velocity to max speed
var normal = normalize([this.velocity.x, this.velocity.y]);
this.velocity = constantMult(normal, this.maxSpeed);
//update position
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
//reset acceleration;
this.acceleration.x = 0;
this.acceleration.y = 0;
}
// target is a vector [x,y]
Boid.prototype.seek = function(target){
var desired = sub(target, [this.position.x, this.position.y]);
var normal = normalize(desired);
desired = constantMult(normal, this.maxSpeed);
var steer = sub(desired,[this.velocity.x, this.velocity.y])
normal = normalize(steer);
steer[0] = normal[0] * this.maxForce;
steer[1] = normal[1] * this.maxForce;
return steer;
}
Boid.prototype.render = function(){
var triangle = drawTriangle(this.velocity);
for(var i = 0; i < triangle.length; i++){
triangle[i] = constantMult(triangle[i], this.r);
}
for(i = 0; i < triangle.length; i++){
triangle[i] = add(triangle[i], this.position);
}
ctx.beginPath();
ctx.moveTo(triangle[0][0], triangle[0][1]);
for(i = 1; i < triangle.length; i++){
ctx.lineTo(triangle[i][0], triangle[i][1]);
}
ctx.closePath();
ctx.fillStyle = "White";
ctx.fill();
}
Boid.prototype.wrap = function(){
if(this.position.x < -this.r)
this.position.x = c.width + this.r;
else if(this.position.x > c.width + this.r)
this.position.x = -this.r;
if(this.position.y < -this.r)
this.position.y = c.height + this.r;
else if(this.position.y > c.height + this.r)
this.position.y = -this.r;
}
Boid.prototype.seperate = function(boids){
var desiredSeperation = 25.0;
var steer = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var d = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(d < desiredSeperation){
var normalDiff = normalize(difference);
normalDiff = constantMult(normalDiff, 1/d);
steer = add(steer, normalDiff);
count++;
}
}
if(count > 0){
steer = constantMult(steer, 1/count);
steer = normalize(steer);
steer = constantMult(steer, this.maxSpeed);
steer = sub(steer, this.velocity);
steer = normalize(steer);
steer = constantMult(steer, this.maxForce);
}
return steer;
}
Boid.prototype.align = function(boids){
var neighborDistance = 50;
var sum = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var dist = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(dist < neighborDistance){
sum = sum(sum, boids[i].velocity);
count++;
}
}
if(count > 0){
sum = constantMult(sum, 1/count);
sum = normalize(sum);
sum = constantMult(this.maxSpeed);
var steer = sub(sum, this.velocity);
steer = normalize(steer);
steer = constantMult(steer, this.maxForce);
return steer;
}
else
return [0,0];
}
Boid.prototype.cohesion = function(boids){
var neighborDistance = 50;
var sum = [0,0];
var count = 0;
for(var i = 0; i < boids.length; i++){
var difference = sub(this.position, boids[i].position);
var dist = Math.sqrt(Math.pow(difference[0],2), Math.pow(difference[1],2));
if(dist < neighborDistance){
sum = add(sum, boids[i].position);
count++;
}
}
if(count > 0){
sum = constantMult(sum, 1/count);
return this.seek(sum);
}
else
return [0,0];
}
//////////////////// HELPERS ///////////////////////
// returns the vector with the same direction as v but with magnitude 1 in the form [x,y]
// v is a vector in the form [x,y]
function normalize(v){
var magnitude = Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2))
var normalX = v[0] / magnitude;
var normalY = v[1] / magnitude;
return [normalX, normalY];
}
function add(a,b){
var x = a[0]+b[0];
var y = a[1]+b[1];
return [x,y];
}
// returns a-b, [ax-bx, ay-by]
function sub(a,b){
var x = a[0]-b[0];
var y = a[1]-b[1];
return [x,y];
}
function mult(a,b){
var x = a[0]*b[0];
var y = a[1]*b[1];
return [x,y];
}
function constantMult(a, n){
for(var i = 0; i < a.length; i++){
a[i] *= n;
}
}
// creates an unscaled issoceles triangle centered at the origin
// returns a list of 3 lists, each containing the coordinates of a vertex, the first being the tip
// ie. [ [x1,y1], [x2,y2], [x3,y3] ]
// heading is a vector describing the direction of the triangle in the form [x,y]
// heading does not need to be normalized
function drawTriangle(heading){
heading = normalize(heading);
var v1 = [1,0];
var v2 = [-1, .5];
var v3 = [-1,-.5];
var thetaX = Math.acos(heading[0]);
var thetaY = Math.asin(heading[1]);
var theta;
if(thetaX >= 0)
theta = (Math.PI / 2) - thetaY;
else
theta = (Math.PI / 2) - thetaX;
function rotate(v){
var xp = (v[0] * Math.cos(theta)) - (v[1] * Math.sin(theta));
var yp = (v[1] * Math.cos(theta)) + (v[0] * Math.sin(theta));
return [xp, yp];
}
v1 = rotate(v1);
v2 = rotate(v2);
v3 = rotate(v3);
return [v1,v2,v3];
}
setup();
draw();
I was able to visualize the aforementioned functions successfully, then I tried to use Object Oriented Programming and compress my code into one function, it seems to not give any result, it doesn't show any errors but in same time, it doesn't display anything except the background when I run it, I am using the p5js library, How can I fix this issue?
I just started working with JS a few months ago, sorry for my ignorance
Here is my code:
var step = 1;
var scl = 1;
//amplitude for each visualization
var sa, ca, ta;
var ssld, csld, tsld;
//to hold x-values and y-values of the three functions
var sx = [], sy = [], cx = [], cy = [], tx = [], ty = [];
function setup() {
angleMode(DEGREES);
createCanvas(640, 480);
background(55);
ssld = createSlider(0, 150, 75);
ssld.position(10, 10);
csld = createSlider(0, 150, 75);
csld.position(10, 35);
tsld = createSlider(0, 150, 75);
tsld.position(10, 60);
var s_ = generateValues("sin");
var c_ = generateValues("cos");
var t_ = generateValues("tan");
sx = s_[0]
sy = s_[1]
cx = c_[0]
cy = c_[1]
tx = t_[0]
ty = t_[1]
}
function draw() {
var sa = ssld.value();
var ca = csld.value();
var ta = tsld.value();
strokeWeight(scl);
stroke(255, 0, 0);
renderWave(sx, sy);
stroke(0, 255, 0);
renderWave(cx, cy);
stroke(0, 255, 255);
renderWave(tx, ty);
}
//given the wave type, it returns a two-dimensional array containing the x-values, and y-values
function generateValues(wave_type) {
var xvalues = [];
var yvalues = [];
for (var i = 0; i < width; i += step) {
append(xvalues, i)
}
if (wave_type === "sin") {
for (var i = 0; i < xvalues.length; i++) {
var y = sin(xvalues[i]);
append(yvalues, y * sa);
}
return [xvalues, yvalues];
} else if (wave_type === "cos") {
for (var i = 0; i < xvalues.length; i++) {
var y = cos(xvalues[i]);
append(yvalues, y * ca);
}
return [xvalues, yvalues];
} else if (wave_type === "tan") {
for (var i = 0; i < xvalues.length; i++) {
var y = tan(xvalues[i]);
append(yvalues, y * ta);
}
return [xvalues, yvalues];
}
}
function renderWave (array_x, array_y) {
for (var i = 0; i < width; i++) {
point(array_x[i], array_y[i] + (height / 2));
}
}
You don't have initial values for sa, ca and ta. So when you call generateValues, those are all undefined, and your y-values are all NaN. Here I've assigned values to them in the setup function, before the call to generateValues. I also removed the var keyword from the assignments in draw(), because you want to use/affect the global variables not create new local variables.
Your sliders don't work - this is because you only use the sa, ca, ta values in generateValues, which you only call on setup, not each time it redraws. You'll need to re-think that part.
var step = 1;
var scl = 1;
//amplitude for each visualization
var sa, ca, ta;
var ssld, csld, tsld;
//to hold x-values and y-values of the three functions
var sx = [], sy = [], cx = [], cy = [], tx = [], ty = [];
function setup() {
angleMode(DEGREES);
createCanvas(640, 480);
background(55);
ssld = createSlider(0, 150, 75);
ssld.position(10, 10);
sa = 10;
csld = createSlider(0, 150, 75);
csld.position(10, 35);
ca = 10;
tsld = createSlider(0, 150, 75);
tsld.position(10, 60);
ta = 10;
var s_ = generateValues("sin");
var c_ = generateValues("cos");
var t_ = generateValues("tan");
sx = s_[0]
sy = s_[1]
cx = c_[0]
cy = c_[1]
tx = t_[0]
ty = t_[1]
}
function draw() {
sa = ssld.value();
ca = csld.value();
ta = tsld.value();
strokeWeight(scl);
stroke(255, 0, 0);
renderWave(sx, sy);
stroke(0, 255, 0);
renderWave(cx, cy);
stroke(0, 255, 255);
renderWave(tx, ty);
}
//given the wave type, it returns a two-dimensional array containing the x-values, and y-values
function generateValues(wave_type) {
var xvalues = [];
var yvalues = [];
for (var i = 0; i < width; i += step) {
append(xvalues, i)
}
if (wave_type === "sin") {
for (var i = 0; i < xvalues.length; i++) {
var y = sin(xvalues[i]);
append(yvalues, y * sa);
}
return [xvalues, yvalues];
} else if (wave_type === "cos") {
for (var i = 0; i < xvalues.length; i++) {
var y = cos(xvalues[i]);
append(yvalues, y * ca);
}
return [xvalues, yvalues];
} else if (wave_type === "tan") {
for (var i = 0; i < xvalues.length; i++) {
var y = tan(xvalues[i]);
append(yvalues, y * ta);
}
return [xvalues, yvalues];
}
}
function renderWave (array_x, array_y) {
for (var i = 0; i < width; i++) {
point(array_x[i], array_y[i] + (height / 2));
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/addons/p5.dom.min.js"></script>
I'm trying to get this particle explosion working. It's working but it looks like some frames does not get rendered. If I click many times to call several explosions it starts to uhm.. "lag/stutter". Is there something I have forgotten to do? It may look like the browser hangs when I click many times. Is it too much to have 2 for loops inside each other?
Attached my code so you can see.
Just try to click many times, and you will see the problem visually.
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
// Canvas
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
// Set full-screen
c.width = window.innerWidth;
c.height = window.innerHeight;
// Options
var background = '#333'; // Background color
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
var explosions = [];
var fps = 60;
var now, delta;
var then = Date.now();
var interval = 1000 / fps;
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
function draw() {
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length == 0) {
return;
}
for (var i = 0; i < explosions.length; i++) {
var explosion = explosions[i];
var particles = explosion.particles;
if (particles.length == 0) {
explosions.splice(i, 1);
return;
}
for (var ii = 0; ii < particles.length; ii++) {
var particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size < 0) {
particles.splice(ii, 1);
return;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
var xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(new explosion(xPos, yPos));
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (var i = 0; i < particlesPerExplosion; i++) {
this.particles.push(new particle(x, y));
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
if (positive == false) {
var num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
} else {
var num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function(e) {
clicked(e);
});
draw();
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>
You are returning from iterating over the particles if one is too small. This causes the other particles of that explosion to render only in the next frame.
I have a working version:
// Request animation frame
const requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
const c = document.getElementById('canvas');
const ctx = c.getContext('2d');
// Set full-screen
c.width = window.innerWidth;
c.height = window.innerHeight;
// Options
const background = '#333'; // Background color
const particlesPerExplosion = 20;
const particlesMinSpeed = 3;
const particlesMaxSpeed = 6;
const particlesMinSize = 1;
const particlesMaxSize = 3;
const explosions = [];
let fps = 60;
const interval = 1000 / fps;
let now, delta;
let then = Date.now();
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
function draw() {
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length === 0) {
return;
}
for (let i = 0; i < explosions.length; i++) {
const explosion = explosions[i];
const particles = explosion.particles;
if (particles.length === 0) {
explosions.splice(i, 1);
return;
}
const particlesAfterRemoval = particles.slice();
for (let ii = 0; ii < particles.length; ii++) {
const particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size <= 0) {
particlesAfterRemoval.splice(ii, 1);
continue;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
explosion.particles = particlesAfterRemoval;
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
let xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(
new explosion(xPos, yPos)
);
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (let i = 0; i < particlesPerExplosion; i++) {
this.particles.push(
new particle(x, y)
);
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
let num;
if (positive === false) {
num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) === 1 ? 1 : -1;
} else {
num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function (e) {
clicked(e);
});
draw();
<!DOCTYPE html>
<html>
<head>
<style>* {margin:0;padding:0;overflow:hidden;}</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>
Loops, break and continue.
The problem was caused when you checked for empty particle arrays and when you found a particle to remove.
The bugs
The following two statements and blocks caused the problem
if (particles.length == 0) {
explosions.splice(i, 1);
return;
}
and
if (particles.size < 0) {
explosions.splice(ii, 1);
return;
}
The returns stopped the rendering of particles, so you would sometimes return before drawing a single particle was rendered just because the first explosion was empty or first particle was too small.
Continue and break
You can use the continue token in javascript to skip the rest of a for, while, do loop
for(i = 0; i < 100; i++){
if(test(i)){
// need to skip this iteration
continue;
}
// more code
// more code
// continue skips all the code upto the closing }
} << continues to here and if i < 100 the loop continues on.
Or you can completely break out of the loop with break
for(i = 0; i < 100; i++){
if(test(i)){
// need to exit the for loop
break;
}
// more code
// more code
// break skips all the code to the first line after the closing }
}
<< breaks to here and if i remains the value it was when break was encountered
The fix
if (particles.length == 0) {
explosions.splice(i, 1);
continue;
}
and
if (particles.size < 0) {
explosions.splice(ii, 1);
continue;
}
Your example with the fix
Your code with the fix. Befor I found it I started changing stuff.
Minor stuff.
requestAnimationFrame passes a time in milliseconds so to an accuracy of micro seconds.
You were setting then incorrectly and would have been losing frames. I changed the timing to use the argument time and then is just set to the time when a frame is drawn.
There are some other issues, nothing major and more of a coding style thing. You should capitalise objects created with new
function Particle(...
not
function particle(...
and your random is a overly complex
function randInt(min, max = min - (min = 0)) {
return Math.floor(Math.random() * (max - min) + min);
}
or
function randInt(min,max){
max = max === undefined ? min - (min = 0) : max;
return Math.floor(Math.random() * (max - min) + min);
}
randInt(100); // int 0 - 100
randInt(10,20); // int 10-20
randInt(-100); // int -100 to 0
randInt(-10,20); // int -10 to 20
this.xv = randInt(-particlesMinSpeed, particlesMaxSpeed);
this.yv = randInt(-particlesMinSpeed, particlesMaxSpeed);
this.size = randInt(particlesMinSize, particlesMaxSize);
And if you are using the same name in variables a good sign to create an object
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
Could be
const settings = {
particles : {
speed : {min : 3, max : 6 },
size : {min : 1 : max : 3 },
explosionCount : 20,
},
background : "#000",
}
Anyways your code.
var c = canvas;
var ctx = c.getContext('2d');
// Set full-screen
c.width = innerWidth;
c.height = innerHeight;
// Options
var background = '#333'; // Background color
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
var explosions = [];
var fps = 60;
var now, delta;
var then = 0; // Zero start time
var interval = 1000 / fps;
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
// as time is passed you need to start with requestAnimationFrame
requestAnimationFrame(draw);
function draw(time) { //requestAnimationFrame frame passes the time
requestAnimationFrame(draw);
delta = time - then;
if (delta > interval) {
then = time
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length == 0) {
return;
}
for (var i = 0; i < explosions.length; i++) {
var explosion = explosions[i];
var particles = explosion.particles;
if (particles.length == 0) {
explosions.splice(i, 1);
//return;
continue;
}
for (var ii = 0; ii < particles.length; ii++) {
var particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size < 0) {
particles.splice(ii, 1);
// return;
continue;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
var xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(new explosion(xPos, yPos));
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (var i = 0; i < particlesPerExplosion; i++) {
this.particles.push(new particle(x, y));
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
if (positive == false) {
var num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
} else {
var num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function(e) {
clicked(e);
});
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>
I want to change the color of the balls to red when they collide. I tried using my function check() to change the color of the balls when they collide using balls[i].color but how do I know the positions of the balls to compare when they collide?
function randomXToY(minVal,maxVal,floatVal)
{
var randVal = minVal+(Math.random()*(maxVal-minVal));
return typeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);
}
// The Ball class
Ball = (function() {
// constructor
function Ball(x,y,radius,color){
this.center = {x:x, y:y};
this.radius = radius;
this.color = color;
this.dx = 2;
this.dy = 2;
this.boundaryHeight = $('#ground').height();
this.boundaryWidth = $('#ground').width();
this.dom = $('<p class="circle"></p>').appendTo('#ground');
// the rectange div a circle
this.dom.width(radius*2);
this.dom.height(radius*2);
this.dom.css({'border-radius':radius,background:color});
this.placeAtCenter(x,y);
}
// Place the ball at center x, y
Ball.prototype.placeAtCenter = function(x,y){
this.dom.css({top: Math.round(y- this.radius), left: Math.round(x - this.radius)});
this.center.x = Math.round(x);
this.center.y = Math.round(y);
};
Ball.prototype.setColor = function(color) {
if(color) {
this.dom.css('background',color);
} else {
this.dom.css('background',this.color);
}
};
// move and bounce the ball
Ball.prototype.move = function(){
var diameter = this.radius * 2;
var radius = this.radius;
if (this.center.x - radius < 0 || this.center.x + radius > this.boundaryWidth ) {
this.dx = -this.dx;
}
if (this.center.y - radius < 0 || this.center.y + radius > this.boundaryHeight ) {
this.dy = -this.dy;
}
this.placeAtCenter(this.center.x + this.dx ,this.center.y +this.dy);
};
return Ball;
})();
var number_of_balls = 5;
var balls = [];
var x;
var y;
$('document').ready(function(){
for (i = 0; i < number_of_balls; i++) {
var boundaryHeight = $('#ground').height();
var boundaryWidth = $('#ground').width();
y = randomXToY(30,boundaryHeight - 50);
x = randomXToY(30,boundaryWidth - 50);
var radius = randomXToY(15,30);
balls.push(new Ball(x,y,radius, '#'+Math.floor(Math.random()*16777215).toString(16)));
}
loop();
check();
});
check = function(){
for (var i = 0; i < balls.length; i++){
for(var j=0;j<balls.length;j++){
if(x==y){
balls[i].color='#ff0000';
alert("y");
}
else{
}
}}
setTimeout(check,8);
};
loop = function(){
for (var i = 0; i < balls.length; i++){
balls[i].move();
}
setTimeout(loop, 8);
};
http://jsbin.com/imofat/743/edit
Calculate the Eucledian distance between the centers of each ball. Then, when this distance is smaller or equal to the sum of their radiuses, there's a collision:
check = function() {
for (var i = 0; i < balls.length; i++) {
for(var j = 0; j < balls.length; j++) {
if (i != j) { // ignore self-collision
if (Math.pow(balls[j].center.x - balls[i].center.x, 2) + Math.pow(balls[j].center.y - balls[i].center.y, 2) <= Math.pow(balls[i].radius + balls[j].radius, 2)) {
balls[j].setColor('red');
} else {
balls[j].setColor(balls[j].color);
}
}
}}
Here's a DEMO.
Edit:
for colliding with other balls, it will require more work.. check this post: Ball to Ball Collision
just setColor when the direction changes, assuming "collide" means "hit the wall":
// move and bounce the ball
Ball.prototype.move = function(){
var diameter = this.radius * 2;
var radius = this.radius;
if (this.center.x - radius < 0 || this.center.x + radius > this.boundaryWidth ) {
this.dx = -this.dx;
this.setColor( 'red' );
}
if (this.center.y - radius < 0 || this.center.y + radius > this.boundaryHeight ) {
this.dy = -this.dy;
this.setColor( 'red' );
}
this.placeAtCenter(this.center.x + this.dx ,this.center.y +this.dy);
};
I added an actual ball collision check inside the check method:
check = function(){
var dx, dy;
var x1, x2, y1, y2, r1, r2;
for (var i = 0; i < balls.length; i++){
x1 = balls[i].center.x;y1=balls[i].center.y;r1=balls[i].radius;
for(var j=0;j<balls.length;j++){
if (i===j) {continue;} // collision with self
x2 = balls[j].center.x;y2=balls[j].center.y;r2=balls[j].radius;
if( Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) <=(r1+r2) ){ // check collision
balls[i].setColor('#ff0000');
}
else{
}
}
}
setTimeout(check,8);
};