• fullscreen
  • bubble.pde
  • tapeworm.pde
  • 
    class Bubble {
      
      float x, y, r;
      int c;
     
      Bubble(float _x, float _y, float _r, int _c) {
        x = _x;
        y = _y;
        r = _r;
        c = _c; 
      }
    
      void move() {
        if(assemble) {
          if(c!=bblcount-1) {
            // attract to sucessor
            Bubble a = getBubble(c + 1);
            float dir = atan2(a.y-y, a.x-x);
            x += speed * cos(dir) ;
            y += speed * sin(dir) ;
          }
        } else y+= 5; // gravity
      }
      
      boolean collide() {
        
        boolean c = false;
      
        // bubble collisions
        for(int i=0; i<bubbles.size(); i++)  {
          c |= collide(getBubble(i));
        }
       
        // wall collisions
        if(x<r)  { x=r ; c = true; }
        if(x>width-r) { x = width-r; c = true; }
        if(y<r) { y = r; c = true; }
        if(y>height-r) { y = height-r; c = true; }
          
        return c;
        
      }
      
      boolean collide(Bubble a) {
        
        if(a == this) return false;  // no self collision
    
        float d = dist(x, y, a.x, a.y);
        float rr =  r + a.r;
        
        // correct bubble positions
        if (d<rr) {
          float t = (rr-d)/rr;
          float dx =  t * (x-a.x);
          float dy =  t * (y-a.y);
          x += dx; y += dy;
          a.x -= dx; a.y -= dy;
          return true;
        }
        return false;
      }
         
      void draw() {
        bubble(x, y, r, c);
      }
      
    }
    
    
    
    //  t a p e w o r m  ///////    ///////  ///////
                             //    //   //  //   //                   
                            ////////   //////   //////// (c) Martin Schneider 2010 ////
    
    
    color  bg0 = 0, bg1 = 255, c1 = #ff6666, c2 = #6666ff;
    boolean dark = true, assemble = true;
    int maxiter = 10;
    int bblsize = 8;
    int cellwall = 8;
    int speed = 5;
    int cycle = 100;
    
    ArrayList bubbles = new ArrayList();
    int bblcount;
    
    void setup() {
      size(500, 500, P2D);
      noStroke();
    }
    
    
    void draw() {
      
      // blur background
      fill(dark ? bg0 : bg1, 50);
      rect(0, 0, width, height);
        
      // move bubbles
      int n = bubbles.size();
      for(int i=0; i<n; i++) getBubble(i).move();
      
      // resolve collisions
      boolean c = true;
      for(int cc=0; cc<maxiter & c; cc++) {
        c = false;
        for(int i=0; i<n; i++) c |= getBubble(i).collide();
      }
     
      // draw bubbles
      for(int i=0; i<n; i++) getBubble(i).draw();
      
      interaction();
      
    }
    
    
    /// user interaction 
    
    void interaction() {  
      if(mousePressed) {
        Bubble b = new Bubble(mouseX, mouseY, bblsize, bblcount);
        if(!b.collide()) {
          bubbles.add(b);
          bblcount++;
        }
      }
    }
    
    
    void keyPressed() {
      switch(key) {
        case 'r': bubbles = new ArrayList(); bblcount = 0; break;
        case ' ': assemble = !assemble; break;
        case 'w': dark = !dark; break;
        case 'f': speed++; break;
        case 's': speed--; break;
      }
      speed = constrain(speed, 1, 2*bblsize);
    }
    
    
    // helper functions
    
    Bubble getBubble(int i) {
      return (Bubble) bubbles.get(i);
    }
    
    void bubble(float x, float y, float r, int n) {
      color c = lerpColor(c1, c2, .5 + sin(n * TWO_PI / cycle) *.5  );
      fill(c, 128); ellipse(x, y, 2*r, 2*r); // cell wall
      fill(c); ellipse(x, y, 2*r-cellwall, 2*r-cellwall); // cell core
    }
    
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    bitcraft
    Steven Kay
    8 Jan 2010
    fascinating!
    DeadDealer
    10 Jan 2010
    it is interesting, that the worm is more stable when it moves faster :)
    bitcraft
    10 Jan 2010
    @DeadDealer: that's ye good olde wagon wheel effect ...
    ( http://en.wikipedia.org/wiki/Wagon-wheel_effect )
    At maximum speed the worm travels exactly one diameter of a single bubble per frame,
    so the bubbles seem to stand still ...
    Giles Whitaker
    11 Jan 2010
    Very nice emergent behaviour. Would you mind explaining this to me:
    for(int cc=0; cc&lt;maxiter & c; cc++)

    What does the single ampersand in "maxiter & c" mean?
    Giles Whitaker
    11 Jan 2010
    Also:

    c |= getBubble(i).collide();

    The |= ...I am unfamiliar with. Thanks.
    bitcraft
    12 Jan 2010
    Hi Giles - those are java logical operators.
    "cc&lt;maxiter & c" means : cc is smaller than maxiter AND c is true.
    And "c |= x" is equivalent to "c = c | x" .
    Jean-no
    14 Jan 2010
    Excellent, with a clean & concise coding.
    Giles Whitaker
    14 Jan 2010
    Yes, I noticed that too. Thanks for the explanations....
    Xavier
    25 Jan 2010
    Very nice efects.
    bejoscha
    18 Feb 2010
    Extremely cool. I especially like to make X shapes...
    Kyle McDonald
    25 Aug 2011
    i'm amazed to see the complex emergent behavior given the simple rule 'follow your successor unless you're first'.
    Funny, when there is a lot of bubbles they seem to 'crystalize'. They form solid, hexagonal pattern and the movement is only on its edges.
    Pierre MARZIN
    23 Jul 2012
    Thatt's a cool one!
    You need to login/register to comment.