// Agent Superclass
class Agent {
PVector loc;
PVector vel;
PVector acc;
int agentIndex;
int closestAnchorID;
int pCount;
String agentType = "Agent";
// Constructor
Agent(float x, float y) {
acc = new PVector(0,0);
vel = new PVector(random(-1,1),random(-1,1));
loc = new PVector(x,y);
agentIndex=0;
closestAnchorID=0;
pCount = 0;
}
// calculates new location
void step(AgentSystem world, int index){
}
// applies repeller force
void applyForce(PVector force) {
float mass = 1; // We aren't bothering with mass here
force.div(mass);
acc.add(force);
}
// world (bounding box)
void borders() {
if(loc.x<0+margin){
vel.x *= -1;
loc.x = 0+margin;
}else if(loc.x>width-margin){
vel.x *= -1;
loc.x = width-margin;
}
if(loc.y<0+margin){
vel.y *= -1;
loc.y = 0+margin;
}else if(loc.y>height-margin){
vel.y *= -1;
loc.y = height-margin;
}
}
} // end of class
// AgentSystem class
// Manages the ArrayList of all the agents
class AgentSystem {
ArrayList population; // an arraylist for all the Agents
AgentSystem() {
population = new ArrayList(); // initialize the arraylist
}
// a function to apply a force to all Particles
void applyForce(PVector f) {
for (int i = 0; i < population.size(); i++) {
Agent a = (Agent) population.get(i);
//if(a.agentType == "Anchor") continue; //Skip Gravity
//if(a.agentType == "Particle") continue; //Skip Gravity
a.applyForce(f);
}
}
// cycles through each agent passing the population to it
void run() {
for (int i = 0; i < population.size(); i++) {
Agent a = (Agent) population.get(i);
a.step(this, i);
}
}
// Hashgrid stuff
void runUpdateHash() {
for (int i = 0; i < population.size(); i++) {
Agent a = (Agent) population.get(i);
if(a.agentType == "Anchor") continue;
hashish.update(a.loc); //updating hashgrid with positions
}
}
// adds an agent to the population
void addAgent(Agent a) {
population.add(a);
}
} // end of class
// Anchor Subclass of Agent
class Anchor extends Agent {
// inherits all instance variables from parent + adding some more
int closestOtherA;
float maxspeed;
// Constructor
Anchor(float x, float y) {
super(x,y) ; // Call the parent constructor
closestOtherA = 0; // Closest other Anchor ID
agentType = "Anchor";
maxspeed = 1;
}
// runs the Anchor
void step(AgentSystem agents, int index){
agentIndex=index;
updatePop(agents.population);
borders();
render(agents.population);
} // end of step
// runs & updates all the calcs.
void updatePop(ArrayList pop) {
pCount = ClacPCount(pop);
//println (pCount);
PVector AttrOA = AttrOtherA(pop); // Attract/repel closest other particle
// Arbitrarily weight these force
AttrOA.mult(wAttrOA);
// adds the force vectors
loc.add(AttrOA);
vel.limit(maxspeed);
vel.add(acc);
vel.mult(0.55); //friction
loc.add(vel);
acc.mult(0);
} // end of update
// count the associtaed particles
int ClacPCount (ArrayList pop) {
pCount = 0;
for(int i = 0; i<pop.size(); i++){
Agent other = (Agent) pop.get(i);
if(other.agentType == "Anchor") continue;
if(this.agentIndex == other.closestAnchorID){
pCount += 1;
}
}
return pCount;
} //end of ClacPCount
// method for attract/repel other Anchor
PVector AttrOtherA (ArrayList pop) {
PVector aoa = new PVector(0,0);
// find closest other
float closestDist = width*height;
closestOtherA = 0;
for(int i = 0; i<pop.size(); i++){
Agent other = (Agent) pop.get(i);
if(other==this) continue;
if(other.agentType == "Particle") continue;
float d = loc.dist(other.loc);
if(d < closestDist){
closestDist = d;
closestOtherA = i;
}
}
Agent otherA = (Agent) pop.get(closestOtherA);
aoa = PVector.sub(otherA.loc, this.loc);
aoa.normalize();
// repel
//if (closestDist < (wPcount*pCount)){
if (closestDist < repelOtherARad+(wPcount*pCount)){
aoa.mult(-1);}
return aoa;
} //end of AttrOtherP
// render method
void render(ArrayList pop) {
ellipseMode(CENTER) ;
fill(#00A485,100);
noStroke();
ellipse(loc.x,loc.y,pCount,pCount);
strokeWeight(3);
stroke(#E31230);
point(loc.x,loc.y);
noStroke();
}// end of Method
} // end of subclass
import controlP5.*;
public ControlP5 control;
public ControlWindow w;
void setParameters() {
//Parameters
repelOtherPRad = 20;
repelOtherARad = 70;
repelAnchRad = 50;
wAttrOA = 1; // weight attraction/repel other anchor
wAttrA = 1; // weight attraction/repel anchor
wAttrP = 1; // weight attraction/repel other particle
wPcount = 1; // weight pCount (partice count influence)
grav = 0.1; // gravity
margin = 60; // margin offset around window edge
pendown = 120; // pen down (alpha channel)
}
void makeControls() {
control = new ControlP5(this);
w = control.addControlWindow("controlWindow", 10, 10, 400, 140);
w.hideCoordinates();
w.setTitle("World Parameters");
int y = 0;
control.addSlider("repelOtherPRad",0,300, repelOtherPRad, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("repelOtherARad",0,300, repelOtherARad, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("repelAnchRad",0,300, repelAnchRad, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("wAttrP",1,5, wAttrP, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("wAttrOA",1,5, wAttrOA, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("wAttrA",1,5, wAttrA, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("wPcount",0.1,5, wPcount, 10,y += 10, 256, 9).setWindow(w);
control.addSlider("grav",-1,1, grav, 10,y += 15, 256, 9).setWindow(w);
control.addSlider("margin",0,(height/3), margin, 10,y += 15, 256, 9).setWindow(w);
control.addSlider("pendown",0,255, pendown, 10,y += 10, 256, 9).setWindow(w);
control.setAutoInitialization(true);
}
// hashgrid class by Ale
// http://www.openprocessing.org/sketch/34290
// text below as written by Ale:
/*H//A hashgrid class
//uk
The purpose of this sketch was to research onto spatial hashing, a technique that allows to optimize collision detection.
In order to get this, we create a grid of 'buckets' and we store the position of the particles in them. Thus when we want to check
the distances, we don't use the entire group, only the particles stored in the buckets nearest to the current particle.
I've based my class on and good ol' sketch of Flux: (http://users.design.ucla.edu/~mflux/p5/hashcollision2/applet/).
I only have updated deprecated classes (LinkedList and HashMap instead of Vector and HashTag) and simplified some methods.
Without this technique 10,000 particles need 10 million of distance checks every frame, with it we only need 200,000.
Amazingly simple and efficient!
//es
*/
class H {
HashMap hM;
float tamCelda;
//Constructor
H (float tamCelda){this.tamCelda=tamCelda;hM=new HashMap();}
int getXr (PVector pos) { //Given a location give me the horizontal position of the bucket that contains it
return int(floor(pos.x/tamCelda));
}
int getYr (PVector pos) { //...
return int(floor(pos.y/tamCelda));
}
String getKey (PVector pos){ //we'll make a string with the bucket position and that'll be our lookout key
if(pos==null) { //i.e.: if we are in the bucket (23,48) --- the key will be 2348
return "";
}else{
return str(getXr(pos))+str(getYr(pos));
}
}
void clear(){ //clear data in the hashmap
hM.clear();
}
void update(PVector pos){ //UPDATE METHOD
if (pos==null){
return;
}else{
String hkey=getKey(pos); //check the key for the particle
if (hM.get(hkey)==null){ //if the key doesn't exist create the container related to it
ArrayList vl=new ArrayList(); //I used LinkedList cause I don't need here any random access to data. If you need so, use ArrayList instead!
vl.add(pos); //add position to the container
hM.put(hkey,vl); //put container into the hashmap
}else{
ArrayList vl= (ArrayList) hM.get(hkey); //if key exists, retrieve the container
vl.add(pos); //and put the position inside
}
}
}
ArrayList check(PVector pos,int sight){ //CHECK METHOD
if (pos==null){
return null;
}else{
int Xr=getXr(pos); //As usual, we check the key related to current particle
int Yr=getYr(pos);
//Sight represents the 'far-sight' of the particle, because maybe we need particles that 'see' further than others
//I.e.: if sight is one, you'll check all buckets that are one bucket far from the current one, including it (9) | two: 25 | etc.
int checkArea=int(pow((sight*2+1),2)); //total number of buckets
String[] keys=new String[checkArea]; //group of keys in the area
int in=0;
for (int xr=Xr-sight;xr<=Xr+sight;xr++){ //we'll walk trough the check area taking all keys, existent or not.
for (int yr=Yr-sight;yr<=Yr+sight;yr++){
PVector c=new PVector(xr,yr);
keys[in++]=str(xr)+str(yr);
}
}
ArrayList checked= new ArrayList();
for (int i=0;i<keys.length;i++){
if(hM.get(keys[i])!=null){ //if key exists (if it's inside the game-board)
checked.addAll((ArrayList)hM.get(keys[i])); //check all positions and add them to the list we want
}
}
return checked;
}
}
}
//////////////////////////////////////////////////////////////
// HOMEOSTAT - EXPLORING EMERGENT VORONOI STRUCTURES //
//////////////////////////////////////////////////////////////
// (c) Marc Fleming 2012 Free Software
// Licensed according to the GNU GPL
// Special thanks goes to Paul Coats & Manos (Emmanouil Zaroukas) for teaching how to programm
// Spatial Hashing class for faster performace by Ale -->
// http://www.openprocessing.org/sketch/34290
// CONTROLS:
// Left mouse click to add anchor point
// Right mouse click to add particles
// REFERENCES:
// http://en.wikipedia.org/wiki/Homeostat
// Programming.Architecture - By Paul Coates - Routledge (2009)
// http://zrks.blogspot.co.uk/
// http://vimeo.com/32155414
// http://vimeo.com/8215665#at=0
import processing.opengl.*;
/////////////////////World Parameters//////////////////////
public int repelOtherPRad; // repel other Particle distance
public int repelOtherARad; // repel other Anchor distance
public int repelAnchRad; // repel Anchor distance
public float wAttrOA; // weight attraction other anchor
public float wAttrA; // weight attraction anchor
public float wAttrP; // weight attraction other particle
public float wPcount; // weight pCount (partice count influence)
public int margin; // margin from the edge of the world
public float grav; // gravity
public int pendown; // pen down
int nPart = 600; // number of initail particles
int nAnch = 30; // number of initail Anchors
// Spatial Hashing Stuff
public H hashish; // hashgrid object
public float ratio=25f; // grid-size and distance to check // 25f
AgentSystem as;
void setup() {
size(800,600,P2D);
//size(1000,600,OPENGL);smooth(); //use OPNENGL to speed up
setParameters();
makeControls();
hashish=new H(ratio);
as = new AgentSystem();
// add an initial set of particles into the system
for (int i = 0; i < nPart; i++) {
as.addAgent(new Particle(random(margin,width-margin),random(margin,height-margin)));
}
// add an initial set of particles into the system
for (int i = 0; i < nAnch; i++) {
as.addAgent(new Anchor(random(margin,width-margin),random(margin,height-margin)));
}
} // end of setup
void draw() {
//background(#E5E5DC); // use if pendown if off
//for pen down
fill (#E5E5DC,pendown);
rect(0,0,width,height);
hashish.clear(); // clear Hashgrid
println(frameRate);
// apply gravity force to all Particles
PVector gravity = new PVector(0,grav); //0.1
as.applyForce(gravity);
as.runUpdateHash();
as.run();
} // end of draw
// add a new Anchor into the System
void mousePressed() {
if (mouseButton == LEFT) {
as.addAgent(new Anchor(mouseX,mouseY));
}
if (mouseButton == RIGHT) {
for (int i = 0; i < 25; i++) {
as.addAgent(new Particle(mouseX+random(20),mouseY+random(20)));
}
}
} // end of void mousePressed
// Particle Subclass of Agent
class Particle extends Agent {
// inherits all instance variables from parent + adding some more
int closestOtherP;
float maxspeed;
// Constructor
Particle(float x, float y) {
super(x,y) ; // call the parent constructor
closestOtherP = 0; // closest other Particle ID
closestAnchorID = 0; // closest Anchor ID
agentType = "Particle";
maxspeed = 1;
}
// runs the Particle class
void step(AgentSystem agents, int index){
agentIndex=index;
updatePop(agents.population);
borders();
render(agents.population);
} // end of step
// runs & updates all the calcs.
void updatePop(ArrayList pop) {
PVector AttrA = AttrAnchor(pop); // attract/repel closest Anchor
PVector AttrP = AttrOtherP(pop); // attract/repel closest other Particle
// arbitrarily weight these forces
AttrA.mult(wAttrA);
AttrP.mult(wAttrP);
// adds the force vectors
loc.add(AttrA);
loc.add(AttrP);
vel.add(acc);
vel.limit(maxspeed);
vel.mult(0.25); // friction
loc.add(vel);
acc.mult(0);
} // end of update
// method for attract/repel Anchor
PVector AttrAnchor (ArrayList pop) {
PVector aa = new PVector(0,0);
// find closest Anchor
float closestDist = width*height;
int closestAnchor = 0;
for(int i = 0; i<pop.size(); i++){
Agent other = (Agent) pop.get(i);
if(other==this) continue;
if(other.agentType == "Particle") continue;
float d = loc.dist(other.loc);
if(d < closestDist){
closestDist = d;
closestAnchor = i;
closestAnchorID = closestAnchor;
}
}
Agent other = (Agent) pop.get(closestAnchor);
aa = PVector.sub(other.loc,this.loc);
aa.normalize();
// particle <--> anchor connection line
if (closestDist > 0){
int a = 100;
strokeWeight(0.5);
stroke(255,0,0,a);
line(this.loc.x,this.loc.y,other.loc.x,other.loc.y);}
///////////////////////////////////////////////
// repel/attract distance
//if (closestDist < repelAnchRad){ // set repel distance
//if (closestDist < (wPcount*other.pCount)){ // pCount is repel/attract distance
if (closestDist < (repelAnchRad+(wPcount*other.pCount))){ // closest Anchor. pCount affects repel/a
aa.mult(-1);}
return aa;
} //end of attrAnchor
// method for attract/repel other Particles
PVector AttrOtherP (ArrayList pop) {
PVector ap = new PVector(0,0);
// find closest other
float closestDist = width*height;
closestOtherP = 0;
PVector Vthis = this.loc;
ArrayList Hashpop = new ArrayList (hashish.check(this.loc,1));
for(int i = 0; i<Hashpop.size(); i++){
PVector other = (PVector) Hashpop.get(i);
float d = Vthis.dist(other);
if(d>0 && d<closestDist){
closestDist = d;
closestOtherP = i;
}
}
PVector other = (PVector) Hashpop.get(closestOtherP);
ap = PVector.sub(other, Vthis);
ap.normalize();
// particle <--> particle connection line
if (closestDist < 200 && closestDist > 0){
int a = 100;
strokeWeight(1);
stroke(#000000,a);
line(Vthis.x,Vthis.y,other.x,other.y);}
///////////////////////////////////////////////
// repel
if (closestDist < repelOtherPRad){
ap.mult(-1);}
return ap;
} // end of AttrOtherP
// render method
void render(ArrayList pop) {
strokeWeight(4);
stroke(#FF0000);
point(loc.x,loc.y);
}// end method
} // end of subclass
Self stabilising (Homeostatic) minimal path tessellation. Uses the principles of the attract/repel algorithm described by Paul Coats in his book Programming.Architecture to emerge a voronoi structure rather than the traditional computational approach. The complexity of the emergent forms can be much higher then defining them in pure geometric ways.
- Use CONTROLS to change parameters:
- Left mouse click to add anchor point
- Right mouse click to add particles