• fullscreen
  • Agent.pde
  • AgentSystem.pde
  • Anchor.pde
  • Controls.pde
  • Hash.pde
  • Homeostat_v2_03.pde
  • Particle.pde
  • // 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
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Marc Fleming

    Homeostat_v2_03

    Add to Faves Me Likey@! 28
    You must login/register to add this sketch to your favorites.

    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

    Marc Fleming
    21 Aug 2012
    Use OpenGL to speed up & smooth(); to improve the render quality.
    Marc Fleming
    21 Aug 2012
    PARAMETERS:

    repelOtherPRad = particle-particle attract repel distance
    repelOtherARad = anchor-anchor attract repel distance
    repelAnchRad = particle-anchor repel distance

    wAttrOA = weight attraction/repel other anchor
    wAttrA = weight attraction/repel anchor
    wAttrP = weight attraction/repel other particle
    wPcount = weight pCount (partice count influence,)

    grav = gravity
    margin = margin offset around window edge
    pendown = pen down (alpha channel - mimics Netlog pendown)
    Raven Kwok plus+
    22 Aug 2012
    Cool sketch indeed. I'm thinking of making each cell split and die autonomously, simulating a growing process.
    Marc Fleming
    22 Aug 2012
    Thanks Raven, looking forward to seeing it. Working on a 3D version at the moment.
    bitcraft
    27 Aug 2012
    Beautiful! That's the kind of emergence that makes my heart beat ( literally )
    Marc Fleming
    15 Sep 2012
    Thank you bitcraft! I find your work very inspiring.
    You need to login/register to comment.