• fullscreen
  • CellStats.pde
  • Environment.pde
  • SlimeCell.pde
  • slimeMoldSim.pde
  • class CellStats
    {
      public final float GRID_SIZE = 5;
      public final float CLOSENESS = 5.0; 
      
      public final int PATTERN = 2;
      
      public int RAD = 3; 
      public float TRAIL = 100;
      public int FADE = 6;
      public float SPEED = 1.09;
      public float SNIFF = 0;
      
      public boolean DRAW_CELLS = false;
      
      CellStats()
      {
      }
      
    }
    
    class Environment
    {
      private PImage IDVals;
      private PImage ground;
      private CellStats stats;
      
      
      Environment(CellStats stats)
      {
        ground = createImage(width, height, ARGB);
        IDVals = createImage(width, height, RGB);
        this.stats = stats; 
      }
      
      void fade()
      {
        ground.loadPixels();
        
        for(int i = 0; i < ground.pixels.length; i++)
        {
          int alph = ((ground.pixels[i]>>24)&0xFF) - stats.FADE;
          alph = max(0, alph);
          ground.pixels[i] = color((ground.pixels[i]>>16)&0xFF, (ground.pixels[i]>>8)&0xFF, (ground.pixels[i])&0xFF, alph);
        }
        
        ground.updatePixels();
        
      }
      
      void startEdit()
      {
        ground.loadPixels();
        IDVals.loadPixels();
      }
      
      void endEdit()
      {
        ground.updatePixels();
        IDVals.updatePixels();
      }
      
      
      void setPixel(int x, int y, int colour, float strength, int ID)
      {
        if(strength > 1)
        {
          if(x < ground.width && y < ground.height && x > 0 && y > 0)
          {
            float alph = alpha(ground.pixels[y*ground.width + x]);
            ground.pixels[y*ground.width + x] = lerpColor(colour, ground.pixels[y*ground.width + x], .5);
            ground.pixels[y*ground.width + x] = color((ground.pixels[y*ground.width + x]>>16)&0xFF, (ground.pixels[y*ground.width + x]>>8)&0xFF, (ground.pixels[y*ground.width + x])&0xFF, strength + alph);
            IDVals.pixels[y*ground.width + x] = ID;
          }
        }
      } 
      
      int getPixel(int x, int y, int ID)
      {
        
        if(x < ground.width && y < ground.height && x > 0 && y > 0)
        {
          if(IDVals.pixels[y*IDVals.width + x] == ID)
          return -1;
          
          return (int)alpha(ground.pixels[y*ground.width + x]);
        }
        
        else return -1;
      }
      
      void draw()
      {
        image(ground,0,0);
      }
      
      
      
      
    }
    
    class SlimeCell
    { 
      private float closeness;
      
      private float myDir = (int)(random(4)+1)*(PI/2.0);
      
      public CellStats stats;
      public float x, y;
      public int gridX = -1, gridY = -1;
      public int cellColour, trailColour;
      
      public boolean sniff;
      private int ID;
      
      private float direction;
      private Environment enviro;
      
      private float speedMult = random(3.5)+1;
      private float sniffOffset = 2.5;
      
      private ArrayList arrX;
      private ArrayList arrY;
      
      SlimeCell(float x, float y, int cellColour, int trailColour, CellStats stats, Environment enviro, int ID)
      {
        this.stats = stats;
        this.place(x,y);
        
        this.cellColour = cellColour;
        this.trailColour = trailColour;
        this.enviro = enviro;
        
        this.ID = ID;
        
        direction = random(TWO_PI);
        
        
      }
      
      void step()
      {
        if(random(1) > stats.SNIFF)
          this.sniff();
        else
          this.direction = random(2) > 1 ? this.direction + random(.1)*PI : this.direction - random(.1)*PI ; //myDir;
          
        this.walk();
      }
      
      void update()
      {
        this.excrete();
        this.draw();
      }
      
      void walk()
      {
        float nX = this.x + cos(direction)*stats.SPEED*speedMult;
        float nY = this.y + sin(direction)*stats.SPEED*speedMult;
       
        this.place(nX, nY); 
      }
      
      void place(float nX, float nY)
      {
        this.x = nX;
        this.y = nY;
        
        this.x = this.x >= width ? 0 : this.x; 
        this.x = this.x < 0 ? width-1 : this.x; 
        
        this.y = this.y >= height ? 0 : this.y; 
        this.y = this.y < 0 ? height-1 : this.y;
        
        int newGridX = (int)floor(this.x/stats.GRID_SIZE);
        int newGridY = (int)floor(this.y/stats.GRID_SIZE);
        
        if((newGridX != this.gridX || newGridY != this.gridY) && gridX >= 0 && gridY >= 0)
        {
          int ind = grid[this.gridX][this.gridY].indexOf(this);
        
          if(ind >= 0)
          {
            grid[gridX][gridY].remove(ind);
            grid[newGridX][newGridY].add(this);
          } 
        }
        else if( gridX < 0 && gridY < 0 )
          grid[newGridX][newGridY].add(this);
          
        gridX = newGridX;
        gridY = newGridY;
      }
      
      void excrete()
      {
        enviro.startEdit();
        
        for(int i = (int)this.x-stats.RAD; i < (int)this.x+stats.RAD; i++)
        {
          for(int j = (int)this.y-stats.RAD; j < (int)this.y+stats.RAD; j++)
          {
      
            float strength = (255-((dist(i,j,x,y)/stats.RAD)*255) + stats.TRAIL)/6.0; 
            strength = min(255, strength);
     
            enviro.setPixel(i,j,trailColour, strength, ID);
            
          }
        }
        
        enviro.endEdit();
      }
      
      void sniff()
      {
        int xP = -1, yP = -1;
        int maxStr = -1;
        
        switch((int)random(stats.PATTERN))
        {
          case 0:
                  for(int i = (int)(this.x-(stats.RAD*sniffOffset)); i < (int)(this.x+(stats.RAD*sniffOffset)); i++)
                  {
                    for(int j = (int)(this.y-(stats.RAD*sniffOffset)); j < (int)(this.y+(stats.RAD*sniffOffset)); j++)
                    {  
                      int tempStr = enviro.getPixel(i,j, ID);
                      if(tempStr > maxStr && tempStr > 0)
                      {
    //                    arr = new ArrayList();
                        maxStr = tempStr;
    //                    arr.add(tempStr);
                        xP = i;
                        yP = j;
                      }
    //                  else if(tempStr == maxStr && tempStr > 0)
    //                  {
    //                    arr.add(tempStr);
    //                  }
                    }
                  }
                  break;
           
           case 1:
                  for(int i =  (int)(this.x+(stats.RAD*sniffOffset)); i > (int)(this.x-(stats.RAD*sniffOffset)); i--)
                  {
                    for(int j = (int)(this.y-(stats.RAD*sniffOffset)); j < (int)(this.y+(stats.RAD*sniffOffset)); j++)
                    {  
                      int tempStr = enviro.getPixel(i,j, ID);
                      if(tempStr > maxStr && tempStr > 0)
                      {
                        maxStr = tempStr;
                        xP = i;
                        yP = j;
                      }
                    }
                  }
                  break;
                  
           case 2:
                  for(int i =  (int)(this.x+(stats.RAD*sniffOffset)); i > (int)(this.x-(stats.RAD*sniffOffset)); i--)
                  {
                    for(int j = (int)(this.y+(stats.RAD*sniffOffset)); j > (int)(this.y-(stats.RAD*sniffOffset)); j--)
                    {  
                      int tempStr = enviro.getPixel(i,j, ID);
                      if(tempStr > maxStr && tempStr > 0)
                      {
                        maxStr = tempStr;
                        xP = i;
                        yP = j;
                      }
                    }
                  }
                  break;
                  
           case 3:
                  for(int i = (int)(this.x-(stats.RAD*sniffOffset)); i < (int)(this.x+(stats.RAD*sniffOffset)); i++)
                  {
                    for(int j = (int)(this.y+(stats.RAD*sniffOffset)); j > (int)(this.y-(stats.RAD*sniffOffset)); j--)
                    {  
                      int tempStr = enviro.getPixel(i,j, ID);
                      if(tempStr > maxStr && tempStr > 0)
                      {
                        maxStr = tempStr;
                        xP = i;
                        yP = j;
                      }
                    }
                  }
                  break;
        }
        
        if(maxStr > 0)
        {
          direction = atan2(yP-y, xP-x);
        }
        
      }
      
      void draw()
      {
        fill(cellColour);
        
        if(stats.DRAW_CELLS)
          ellipse(x,y,stats.RAD*2, stats.RAD*2);
      }
      
      public boolean testAndRepel(SlimeCell other)
      {
        closeness = stats.RAD-(stats.RAD/stats.CLOSENESS);
        
        if(dist(this.x, this.y, other.x, other.y) < closeness)
        {
          float ang = atan2(other.y - this.y, other.x - this.x) + PI;
          float nX = other.x + cos(ang)*closeness;
          float nY = other.y + sin(ang)*closeness;
          
          this.place(nX, nY);
          
          return true;
        }
        else
         return false;
      }
    }
    
    //import processing.opengl.*;
    
    import controlP5.*;
    
    
    PFont font;
    
    ControlP5 controlP5;
    //ControlWindow controlWindow;
    
    int cellCount = 3000;
    
    ArrayList[][] grid;
    
    CellStats stat = new CellStats();
    SlimeCell[] slime = new SlimeCell[cellCount];
    Environment enviro;
    
    
    
    void setup()
    {
      size(600, 600);
      
    //  font = loadFont("arahoni.vlw"); 
    //  textFont(font, 32); 
      
      //cellCount/=4;
      println((int)floor(width/stat.GRID_SIZE));
      
      grid = new ArrayList[(int)floor(width/stat.GRID_SIZE)][(int)floor(height/stat.GRID_SIZE)];
      enviro = new Environment(stat);
    
      for(int i = 0; i < grid.length; i++)
      {
        for(int j = 0; j < grid[i].length; j++)
        {
          grid[i][j] = new ArrayList();
        }
      }
    
      for(int i = 0; i < slime.length; i++)
      {
        slime[i] = new SlimeCell(random(width), random(height), color(255,0,0), color(150,220,255), stat, enviro, i);
        slime[i].update();
      } 
    
      noStroke();
    
      setupController();
    }
    
    
    void draw()
    {
      background(40);
      enviro.fade();
      enviro.draw();
    
      for(int i = 0; i < cellCount; i++)
      {
        slime[i].step();
      }
    
      gridCheck();
    
      for(int i = 0; i < cellCount; i++)
      {
        slime[i].update();
      }
      
      //save("/Volumes/scratch/snaps2/"+frameCount+".jpg");
      
    }
    
    
    
    public void controlEvent(ControlEvent theEvent) {
      //println(theEvent.controller().id());
      switch(theEvent.controller().id()) {
    
        case(1):
        cellCount = (int)(theEvent.controller().value());
        break;
    
        case(2):
        stat.RAD = (int)(theEvent.controller().value());
        break; 
    
        case(3):
        stat.FADE = (int)(theEvent.controller().value());
        break;  
    
        case(4):
        stat.SNIFF = (theEvent.controller().value());
        break;
    
        case(5):
        stat.SPEED = (theEvent.controller().value());
        break;  
    
        case(6):
        stat.TRAIL = (theEvent.controller().value());
        break;  
     
        case(7):
        stat.DRAW_CELLS = !stat.DRAW_CELLS;
        break;     
      }
    }
    
    
    void setupController()
    {
      controlP5 = new ControlP5(this);
    
      //controlWindow = controlP5.addControlWindow("controlP5window",300,300,200,200);
      //controlWindow.hideCoordinates();
      //controlWindow.setBackground(color(40));
    
      Controller mySlider;
    
      mySlider = controlP5.addSlider("cell count",10,slime.length,cellCount,10,20,100,14);
      //mySlider.setWindow(controlWindow);
      mySlider.setId(1);
    
      mySlider =controlP5.addSlider("radius",1,30,10,10,40,100,14);
      //mySlider.setWindow(controlWindow);
      mySlider.setId(2);
    
      mySlider =controlP5.addSlider("fade",1,255,10,10,60,100,14);
      //mySlider.setWindow(controlWindow);
      mySlider.setId(3);
    
      mySlider =controlP5.addSlider("sniff",0,1,0,10,80,100,14);
      //mySlider.setWindow(controlWindow); 
      mySlider.setId(4);
    
      mySlider =controlP5.addSlider("speed",.1,10,1.09,10,100,100,14);
      //mySlider.setWindow(controlWindow); 
      mySlider.setId(5);
    
      mySlider =controlP5.addSlider("trail",0,100,100,10,120,100,14);
      //mySlider.setWindow(controlWindow); 
      mySlider.setId(6);
      
      mySlider =controlP5.addButton("Toggle cell drawing", 1, 10, 140, 100, 14) ;
      //mySlider.setWindow(controlWindow); 
      mySlider.setId(7);
    }
    
    
    
    SlimeCell initialCell;
    SlimeCell otherCell;
    
    void gridCheck()
    {
      for(int i = 0; i < grid.length; i++)
      {
        for(int j = 0; j < grid[i].length; j++)
        {
    
          for(int k = 0; k < grid[i][j].size(); k++)
          {
            initialCell = (SlimeCell)grid[i][j].get(k);
            
            for(int m = 0; m < grid[i][j].size(); m++)
            {
              otherCell = (SlimeCell)grid[i][j].get(m);
              if(otherCell != initialCell) 
                initialCell.testAndRepel(otherCell);
            }
              
            if(i < grid.length-1)
              for(int l = 0; l < grid[i+1][j].size(); l++)
              {
                otherCell = (SlimeCell)grid[i+1][j].get(l);
                initialCell.testAndRepel(otherCell);
              } 
    
    
            if(i > 0 && j < grid[i].length-1)
              for(int l = 0; l < grid[i-1][j+1].size(); l++)
              {
                otherCell = (SlimeCell)grid[i-1][j+1].get(l);
                initialCell.testAndRepel(otherCell);
              }
    
            if(j < grid[i].length-1)
            {
              for(int l = 0; l < grid[i][j+1].size(); l++)
              {
                otherCell = (SlimeCell)grid[i][j+1].get(l);
                initialCell.testAndRepel(otherCell);
              }
    
              if(i < grid.length-1)
                for(int l = 0; l < grid[i+1][j+1].size(); l++)
                {
                  otherCell = (SlimeCell)grid[i+1][j+1].get(l);
                  initialCell.testAndRepel(otherCell);
                }
            }
            
            
          }
        }
      }
    }
    
    void mousePressed(){
      
     save("img.jpg"); 
    }
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Ben Jack

    Slime mold sim

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

    This was inspired while reading a description of slime mold in the book "emergence" by Steven Johnson.
    I didn't actually look at other implementations of computerized slime mold simulations as they all seemed more scientific rather than aesthetically oriented.

    It was the initial sketch that my interactive installation was based on:

    http://vimeo.com/16329472

    hey Ben,
    would it stabilize like this: http://www.youtube.com/watch?v=DZ2X7CNXk8Q&feature=related ?
    btw. when i touch the sliders it crashes: you might need controlEvent function just for the browser but i'm not sure...
    mile combo
    26 Aug 2014
    Hi Ben/ Hi Jack, I love the idea underlying your neurofeedback application: as eeg synchrony emerges, particles clusters as a slime mold, and a ringing oscillation kicks in: this is just great! can you tell me more how did you implement focus/concentration from your 4 eeg channels?
    You need to login/register to comment.