// title: BoidRoids Class // author: xffff // description: // Port of the "BoidRoids.js" file contained in the Max distribution examples. // Used with permission of Cycling '74 // // Thought this might be a good example to put up here on writing classes. // code: /* SuperCollider port of BoidRoids.js from the Max examples Used with permission of Cycling '74 (thanks!) Mike Murphy 2012 */ // mode \average = "average x/y/vx/vy list" // mode \normal = "series of x/y/vx/vy lists" BoidRoids { // global varables and code var agents, myagentcount = 0, mode = \normal, x = 0, y = 0, vx = 0, vy = 0; *new{ |numboids| ^super.new.initagents(numboids); } initagents{ |n| // clip agentcount to range 1.-1000. myagentcount = n.clip(1,1000); agents = Array.new(n); for(0,myagentcount-1,{ |i| // start with random position/velocity x = rrand(0.0,1.0); y = rrand(0.0,1.0); vx = (rrand(0.0,1.0)-0.5)*0.1; vy = (rrand(0.0,1.0)-0.5)*0.1; // create a new agent... agents[i][x,y,vx,vy] agents.add([x,y,vx,vy]); } ); } // task function getBoids { //var i; var cx=0; var cy=0; var cvx=0; var cvy=0; for (0,myagentcount-1,{ |i| this.agent_tick(i); // calculate current frame's average position/velocity cx = cx + agents[i][0]; cy = cy + agents[i][1]; cvx = cvx + agents[i][2]; cvy = cvy + agents[i][3]; } ); centroid_x = cx/myagentcount; centroid_y = cy/myagentcount; avgvelocity_x = cvx/myagentcount; avgvelocity_y = cvy/myagentcount; if(mode==\normal,{ ^agents; },{ if(mode==\average,{ ^[centroid_x,centroid_y,avgvelocity_x,avgvelocity_y]; },{ postln("BoidRoids: mode can be either \average, or \normal"); } ); } ); } // one iteration of the simulation for a given agent // we pass this the index of an agent in the agents array // it has the attributes agent[x,y,vx,vy]... agent_tick { | agent | var px,py; // save current velocity for inertia calc //px = vx; //py = vy; px = agents[agent][2]; py = agents[agent][3]; // apply rules this.separate(agent); this.align(agent); this.cohere(agent); this.gravitate(agent); // inertia //vx = (px*myinertia) + (vx*(1-myinertia)); //vy = (py*myinertia) + (vy*(1-myinertia)); agents[agent][2] = (px*myinertia) + (agents[agent][2]*(1-myinertia)); agents[agent][3] = (px*myinertia) + (agents[agent][3]*(1-myinertia)); // velocity limit agents[agent][2] = agents[agent][2].clip(mymaxvel.neg,mymaxvel); agents[agent][3] = agents[agent][3].clip(mymaxvel.neg,mymaxvel); // update position based on velocity and friction //x = x + (vx*(1-myfriction)); //y = y + (vy*(1-myfriction)); agents[agent][0] = agents[agent][0] + (agents[agent][2]*(1-myfriction)); agents[agent][1] = agents[agent][1] + (agents[agent][3]*(1-myfriction)); this.twrap(agent); // torus space } // rules (a is the index of an agent in the agents array) separate { |a| var dx,dy,proxscale,mag; // run from positions of neighbors for(0,myagentcount-1,{ |i| if(a != i,{ dx = agents[i][0] - agents[a][0]; dy = agents[i][1] - agents[a][1]; //torus space if(dx>0.5,{ dx = dx - 1; },{ if(dx<0.5.neg,{ dx = dx + 1; }); }); if(dy>0.5,{ dy = dy - 1; },{ if(dy<0.5.neg,{ dy = dy + 1; }); }); if((abs(dx)>1e-4)&&(abs(dy)>1e-4),{ mag = (dx*dx)+(dy*dy); // cheap mag, no sqrt },{ mag = 0.01; }); if(mag1,{ agents[a][0] = agents[a][0] - 1; }); }); if(agents[a][1]<0,{ agents[a][1] = agents[a][1] + 1; },{ if(agents[a][1]>1,{ agents[a][1] = agents[a][1] - 1; }); }); } // not in use atm bounce { |a| if ((agents[a][0]<0)||(agents[a][0]>1), { agents[a][2] = agents[a][2].neg; }); if ((agents[a][1]<0)||(agents[a][1]>1), { agents[a][3] = agents[a][3].neg; }); } // set var vunctions separation { |v| myseparation = v.clip(0,1)*0.1; } alignment { |v| myalignment = v.clip(0,1)*0.1; } coherence { |v| mycoherence = v.clip(0,1)*0.1; } friction { |v| myfriction = v.clip(0,1); } inertia { |v| myinertia = v.clip(0,1); } septhresh { |v| mysepthresh = v.clip(0,0.5); } maxvel { |v| mymaxvel = v.clip(0,1)*0.1; } gravity { |v| mygravity = v.clip(-1,1)*0.1; } gravpoint { |x,y| mygravpoint_x = x.clip(0,1); mygravpoint_y = y.clip(0,1); } }