xxxxxxxxxx
let tree;
let t = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
// createCanvas(200, 200);
background(100);
colorMode(HSB,360,100,100,100);
tree = new Tree();
tree.grow(4,120,0.8);
}
function draw() {
background(0,0,100,100);
let dt = 0.16;
if(frameRate()>0){
dt = 1.0/frameRate()
}
tree.evolve(dt);
tree.draw();
tree.drawNodes();
//noLoop();
t += dt;
}
function Tree(parent,stride){
if(parent === undefined) {
let p = createVector(width*0.5,height*0.5);
let v = createVector(0.0,1.0);
this.node = new Agent(p,v);
}
else {
this.parent = parent;
let dp = parent.node.v.copy();
dp.normalize().mult(stride);
let p = p5.Vector.add(parent.node.p, dp);
let v = parent.node.v.copy()
let range = PI;
v.rotate(random(-range,range));
this.node = new Agent(p,v);
}
this.node.elasticRange = stride;
this.children = [];
this.grow = function(depth,stride,decay) {
if(depth>0){
this.children = [new Tree(this,stride),new Tree(this,stride),new Tree(this,stride)];
let c;
//console.log(depth);
for(c in this.children){
let child = this.children[c];
child.grow(depth-1,stride*decay,decay);
}
}
//let rand = random(10);
}
this.draw = function() {
if(this.parent === undefined){
}
else {
stroke(0,0,0,60);
noFill();
line(this.parent.node.p.x,this.parent.node.p.y,this.node.p.x,this.node.p.y)
}
if(this.children.length == 3) {
let a = this.children[0].node.p;
let b = this.children[1].node.p;
let c = this.children[2].node.p;
noStroke();
fill(this.node.hue,100,100,5);
triangle(this.node.p.x,this.node.p.y, a.x , a.y , b.x , b.y);
triangle(this.node.p.x,this.node.p.y, b.x , b.y,c.x,c.y);
triangle(this.node.p.x,this.node.p.y, a.x , a.y , c.x , c.y);
}
let c;
for(c in this.children){
let child = this.children[c];
child.draw();
}
}
this.drawNodes = function(){
//ellipse(this.p.x,this.p.y,7,7);
this.node.draw();
let c;
for(c in this.children){
let child = this.children[c];
child.drawNodes();
}
}
this.evolve = function(dt) {
//evolve here
if(this.parent !== undefined){
let siblings = this.parent.children.filter(function(x) { return x != this });
//siblings, pareant and grandchildren?
let nieghbors = siblings.concat(this.children)
nieghbors.push(this.parent);
let nieghboringNodes = nieghbors.map( x => x.node )
this.node.evolve(dt,nieghboringNodes);
}
//then evolve children
let c;
for(c in this.children){
let child = this.children[c];
child.evolve(dt);
}
}
}
function Agent(p,v) {
//position in units of pixels
this.p = p;
this.lastP = p.copy();
//velocity in units of pixels/second;
this.v = v;
this.anchor = createVector(0.5*width ,0.5*height);
this.hue = 360 - random(120);
//accelration in units of pixels/seccond^2
this.a = createVector(0.0,0.0);
this.radius = 3;
this.mass = 1;
this.alignRate = 50;
this.seperationRate = 1000;
this.cohesionRate = 100;
this.maxforce = 100.0;
this.maxVelocity = 200.0;
this.elasticRange = 100;
this.elasticConstant = 0.1;
this.dragConstant = 0.002;
this.evolve = function(dt,flock) {
this.behavior(flock);
this.dynamics(dt);
this.constrain();
}
this.constrain = function() {
if((this.p.x > width && this.v.x > 0.0) || (this.p.x < 0 && this.v.x < 0.0 ) ) {
this.v.x *= -1;
}
if((this.p.y > height && this.v.y > 0.0) || (this.p.y < 0 && this.v.y < 0.0 ) ) {
this.v.y *= -1;
}
if(this.v.mag()>this.maxVelocity) {
this.v.normalize().mult(this.maxVelocity);
}
}
this.behavior = function(swarm){
let forces = [];
//collect all the forces in an array
forces.push(this.separation(swarm));
forces.push(this.align(swarm));
//forces.push(this.cohesion(swarm));
///forces.push(this.attractTo(this.anchor,2000));
let parent = swarm[swarm.length-1];
forces.push(this.elastic(parent.p));
forces.push(this.drag());
forces.push(this.mouse());
forces.push(this.noise());
//add up all the forces
let force = forces.reduce((sum, F ) => sum.add(F) , createVector(0.0,0.0));
//calculate acceleration F=ma
this.a = p5.Vector.div(force,this.mass);
}
this.dynamics = function(dt) {
this.lastP = this.p.copy();
let dv = p5.Vector.mult(this.a,dt);
this.v.add(dv);
let dp = p5.Vector.mult(this.v,dt);
this.p.add(dp);
}
/* ----behaviors ----- */
this.elastic = function(p) {
let d = p.dist(this.p) - this.elasticRange;
let dir = p5.Vector.sub(p,this.p);
dir.normalize();
dir.mult(this.elasticConstant*d);
return dir;
}
this.drag = function() {
let n = this.v.copy().normalize()
let drag = this.dragConstant*sq(this.v.mag());
return n.mult(-drag);
}
this.mouse = function() {
if(mouseIsPressed){
let p = createVector(mouseX,mouseY);
return this.attractTo(p,1000);
}
else {
return createVector(0,0);
}
}
this.separation = function(swarm) {
let count = 0;
let seperationRange = 70;
let p = this.p;
let sum = swarm.reduce(
function(total, thing){
let d = thing.p.dist(p)
if( d>0 && d < seperationRange ) {
count++
let dp = p5.Vector.sub(p,thing.p);
dp.normalize();
dp.div(d);
return total.add(dp);
}
else {
return total;
}
}
, createVector(0.0,0.0));
if (count > 0) {
sum.div(count);
}
if(sum.mag() > 0.0){
sum.normalize();
sum.mult(this.seperationRate);
sum.sub(this.v);
sum.limit(this.maxForce);
}
return sum;
}
this.align = function(swarm) {
let count = 0;
let neighborhood = 100;
let p = this.p;
let sum = swarm.reduce(
function(total, thing){
let d = thing.p.dist(p)
if( d>0 && d < neighborhood ) {
count++
return total.add(thing.v);
}
else {
return total;
}
}
, createVector(0.0,0.0));
if (count > 0) {
sum.div(count);
sum.normalize();
sum.mult(this.alignRate);
sum.sub(this.v);
sum.limit(this.maxForce);
return sum;
}
else{
return createVector(0.0,0.0) ;
}
}
this.cohesion = function(swarm) {
let count = 0;
let cohesionRange = 100;
let p = this.p
let sum = swarm.reduce(
function(total, thing){
let d = thing.p.dist(p)
if( d>0 && d < cohesionRange ) {
count++
return total.add(thing.p);
}
else {
return total;
}
}
, createVector(0.0,0.0));
if(count>0){
let aveP = p5.Vector.div(sum,count);
return this.attractTo(aveP,this.cohesionRate);
}
else {
return createVector(0.0,0.0)
}
}
this.noise = function() {
//add some noise
let nScale = 7;
let s = noise(nScale*this.p.x/width,nScale*this.p.y/height,t);
let dx = p5.Vector.fromAngle(s*TWO_PI);
dx.mult(20);
;
return dx;
}
this.attractTo = function(attractor,rate){
//rate is the the time to close half the distance to the attractor, assuming zero inital velocity, unit mass and no other forces.
let attraction = p5.Vector.sub(attractor,this.p);
attraction.mult(rate);
let vRate = 100;
let v_force = p5.Vector(this.v, vRate);
let force = p5.Vector.sub(attraction,v_force);
force.limit(this.maxforce); // Limit to maximum steering force
return force;
}
this.draw = function(graphicContext) {
fill(this.hue,50,100,100);
stroke(0,0,0,100);
ellipse(this.p.x,this.p.y, 2*this.radius, 2*this.radius);
}
this.drawTrail = function(graphicContext) {
graphicContext.noFill();
graphicContext.stroke(this.hue,100,100,10);
graphicContext.line(this.lastP.x,this.lastP.y,this.p.x,this.p.y);
}
}