Is it possible to make a random growing grid?

edited November 2015 in How To...

Hello,

I'm trying to make a random growing grid that grows on mouseover, but I can't figure out the algorithms. Can somebody help me? I'm trying to create my animation in processing:

Help is much apreciated! Greetings

Tagged:

Answers

  • Is it possible? Sure. What have you tried so far?

    Start smaller. Maybe try just displaying a grid that doesn't grow first? Then make it so you can toggle each cell on or off. When you have that working, then think about adding the growing part.

  • It would probably be easiest to start with a sketch where the width and height are both powers of two. That way you can always subdivide sections in half (both on x and y axii)

    As KevinWorkman mentioned, it would probably be best not to deal with dynamic growing at first, just get a fixed width / height sketch to subdivide

  • edited December 2015

    Thanks! Well, i'm new so i'm doing this hardcoded... And I'm trying to add the grow modus but i get stuck in there as well.

    float a = 0.0;
    float s = 0.0;
    
    void setup()
    {
      size(300, 600);
      background(255);
      noStroke();
      frameRate(30);
    
      a = a + 0.04;
      s = cos(a)*2;
    
      // first row
      // small rectangles
      scale(s);
      fill(0);
      rect(10, 10, 20, 20);
    
      // small rectangles
      fill(0);
      rect(40, 10, 20, 20);
    
      // small rectangles
      fill(0);
      rect(40, 40, 20, 20);
    
      // big rectangles
      fill(0);
      pushMatrix();
      rect(70, 10, 50, 50);
      popMatrix();
    
      // small rectangles
      fill(0);
      rect(10, 70, 20, 20);
    
      // second row
    
      // small rectangles
      fill(0);
      rect(40, 70, 20, 20);
    
      // small rectangles
      fill(0);
      rect(10, 100, 20, 20);
    
      // small rectangles
      fill(0);
      rect(40, 100, 20, 20);
    
      // big rectangles
      fill(0);
      rect(70, 70, 50, 50);
    
      //third row
      // large rectangles
      fill(0);
      rect(10, 130, 110, 110);
    }
    

    So now I probably have to automate the above...

  • (please highlight code, press ctrl-o to format it before posting)

  • did you notice that the structure is recursive?

    we see 4x4 tiles, one tile is taken and split. Of those 4 sub tiles one is split up again...

  • Instead of hard-coding this, maybe use an ArrayList of Square objects?

    The Square objects would be a class you create that contains all of the information you need to draw a square- its position and size. Then it'll be easier to substitute a square's "children" in.

    You could also use a 2D array, but the ArrayList approach seems easier to me. It's really up to you.

  • I'd like to see this in 3D

  • Thank you for thinking with me, but I couldn't fix it on this way because of my my lack of knowledge and I had not much time. Thnx anyway!

  • Answer ✓

    I took a stab at this. What I did was make a class named Box which behaves like a Node of a Tree in the sense that every Box instance has four children which are also Box instances. I arbitrarily chose four as the smallest length that a Box instance could have (because Boxes smaller than that are hard to see)

    When I add a single Box instance below in setup this actually adds a lot of Box instances in a Tree-like structure. To make things simpler I made the sketch a square so that I only had to add one Box instance. Below is the code:

    ArrayList<Box> boxes;
    
    void setup() {
      // Notice that both width and height are a power of two. This is important because a
      // power of two divided by two will still be divisble by two until the smallest Box
      // has a length of four (I arbitrarily picked four as the smallest length a Box
      // instance can have)
      size(512, 512);
    
      // This ArrayList represents all active Box instances
      boxes = new ArrayList<Box>();
    
      // This actually makes a lot of Box instances due to recursion. Each Box instance will
      // make four more Box instances. Each of those will make four Box instances. Each of
      // those will futher make four Box instances and so on until they make children with a
      // length of four (which will terminate the recursion)
      boxes.add(new Box(512, 0, 0));
    }
    
    void draw() {
      background(255);
    
      // Display all active Box instances
      for (Box b : boxes)
        rect(b.x, b.y, b.len - 1, b.len - 1);
    }
    
    void mousePressed() {
      // I am iterating backwards because clicking will probably remove a Box instance and
      // when that happens I will replace it with its children (backwards iteration makes
      // this easier to deal with because index i still refers to the original Box instance)
      for (int i = boxes.size() - 1; i >= 0; i--) {
        Box iBox = boxes.get(i);
    
        // Because I arbitarily chose four as the length size a Box instance can have I do
        // not check inside Box instances with a length of 4 (or less)
        if (iBox.len > 4 && iBox.isInside(mouseX, mouseY)) {
          // Add all four children
          for (int j = 0; j < 4; j++)
            boxes.add(iBox.children[j]);
    
          // Remove the Box instance that added children
          boxes.remove(i);
    
          // Don't bother to check other Box instances, one already added its children
          break;
        }
      }
    }
    
    // This Box class is used to represent your divisible grid. Each Box instance has four
    // children unless its length is four
    class Box {
      final Box[] children;
      final int len, x, y;
    
      Box(final int len, final int x, final int y) {
        this.children = new Box[4];
        this.len = len;
        this.x = x;
        this.y = y;
    
        if (len == 4)
          for (int i = 0; i < 4; i++)
            children[i] = null;
        else {
          final int halfLen = len / 2;
          children[0] = new Box(halfLen, x, y);                     // Top left
          children[1] = new Box(halfLen, x + halfLen, y);           // Top right
          children[2] = new Box(halfLen, x, y + halfLen);           // Bottom left
          children[3] = new Box(halfLen, x + halfLen, y + halfLen); // Bottom right
        }
      }
    
      boolean isInside(final int x, final int y) {
        return x >= this.x && x < this.x + this.len && y >= this.y && y < this.y + this.len;
      }
    }
    

    Below is the same code except that the width is double the height and so I make two Box instances:

    ArrayList<Box> boxes;
    
    void setup() {
      size(1024, 512);
      boxes = new ArrayList<Box>();
      boxes.add(new Box(512, 0, 0));
      boxes.add(new Box(512, 512, 0));
    }
    
    void draw() {
      background(255);
      for (Box b : boxes)
        rect(b.x, b.y, b.len - 1, b.len - 1);
    }
    
    void mousePressed() {
      for (int i = boxes.size() - 1; i >= 0; i--) {
        Box iBox = boxes.get(i);
        if (iBox.len > 4 && iBox.isInside(mouseX, mouseY)) {
          for (int j = 0; j < 4; j++)
            boxes.add(iBox.children[j]);
          boxes.remove(i);
          break;
        }
      }
    }
    
    class Box {
      final Box[] children;
      final int len, x, y;
    
      Box(final int len, final int x, final int y) {
        this.children = new Box[4];
        this.len = len;
        this.x = x;
        this.y = y;
    
        if (len == 4)
          for (int i = 0; i < 4; i++)
            children[i] = null;
        else {
          final int halfLen = len / 2;
          children[0] = new Box(halfLen, x, y);
          children[1] = new Box(halfLen, x + halfLen, y);
          children[2] = new Box(halfLen, x, y + halfLen);
          children[3] = new Box(halfLen, x + halfLen, y + halfLen);
        }
      }
    
      boolean isInside(final int x, final int y) {
        return x >= this.x && x < this.x + this.len && y >= this.y && y < this.y + this.len;
      }
    }
    

    If you have questions I will be happy to answer

  • edited December 2015

    Wow, thank you! I'm going to take a good look at it! Thank you so mich for your time!

  • Answer ✓
    // https : // forum.processing.org/two/discussion/comment/56853#Comment_56853
    
    // squares 
    ArrayList<Square> squares = new ArrayList(); 
    
    // their size
    final int RECT_WIDTH = 100-4;        // the inner size
    final int RECT_HEIGHT = RECT_WIDTH;
    
    final int RECT_WIDTH2 = 100;    // the outer size 
    final int RECT_HEIGHT2 = RECT_WIDTH2 ; 
    
    // this is the offset for each of the 4 sub rects (children rects) 
    // pairs of x,y like -1,1 or -1,-1 etc. 
    PVector[] offset = new PVector[4];   
    
    // from top
    final int borderY=50;   // (200 for height 600, 50 for height 300) 
    
    // states
    final int MAIN              = 0; // MAIN 
    final int showSquaresInRed  = 1; // red 
    int state = MAIN; 
    
    int startTime; 
    int waitTime = 1000; 
    
    // --------------------------------------------
    // Core functions: setup() and draw()  
    
    void setup() {
      size(603, 400);
      background(0);
    
      // see above for explanation 
      offset[0] = new PVector(-1, -1); 
      offset[1] = new PVector(-1, 1); 
      offset[2] = new PVector(1, -1); 
      offset[3] = new PVector(1, 1);
    
      mySetup();
    } // func 
    
    void draw() {
      // depending on state 
      switch(state) {
    
      case MAIN: 
        // state MAIN
        background(0);
        whiteBackgroundBar();
    
        for (Square s : squares ) { 
          s.display();
          s.incrementWH(); // grow
        }
    
        // if grow process is finished 
        if (allAreFinished()) {
          // move on
          state=showSquaresInRed; // next state
          // make all rects red
          for (Square s : squares ) {
            // make all rects red 
            s.RectClassColor=color( 255, 0, 0 );
          } // for
          startTime=millis();
        } // if
        break; 
    
      case showSquaresInRed:
        // state showSquaresInRed
        //
        background(0);
        whiteBackgroundBar();    
        for (Square s : squares ) {
          s.display();
        }
        if (millis() - startTime >= waitTime) {
          // reset 
          squares.clear();
          mySetup();
          state = MAIN;
        } // if 
        break;
    
      default: 
        println ("big error 468 with state being " + state);
        exit();
        break; 
        //
      } // switch
      //
    } // func 
    
    // ----------------------------------------
    // help funcs for setup()
    
    void mySetup() { 
    
      // add new rects  
      for (int x=0; x < 6; x++) {
        for (int y=0; y < 2; y++) {
          // here we assign 4 values to an array giving the depth of recursion for each rect
          int[] depthMap=getDepthMap();
          // now instead of adding a square to the ArrayList we call a function 
          make4Squares(x*RECT_WIDTH2+1+RECT_WIDTH2/2, 
            y*RECT_HEIGHT2+RECT_HEIGHT2/2+borderY, 
            RECT_WIDTH, 
            RECT_HEIGHT, 
            depthMap, 
            0);
        } // for
      } // for
    } // func 
    
    int[] getDepthMap() {
      // here we assign 4 values to an array giving the depth for each rect
      int[] buffer = new int[4];
      for (int i=0; i < 4; i++) { 
        buffer[i]=int(random(-2, 4));  
        // buffer[i]=int(random(-1, 8));   
        // buffer[i]=int(random(-2, 5));
        // buffer[i]=2;
      } // for 
      return buffer;
    } // func 
    
    void make4Squares(float x, float y, 
      float  widthLocal, float  heightLocal, 
      int[] depthMap, 
      int depth) {
    
      // this func is supposed to be recursive 
    
      // looping over 4 child rects 
      for (int i=0; i < 4; i++) {
    
        boolean done=false; 
    
        if (depthMap[i] <= -1) { // try <= -1 to skip some rects 
          // skip
        }
    
        // have we reached our appointed depth (as stored in depthMap[i]) ? 
        else if (depth<depthMap[i]) { 
          // no
          // we need to go deeper
          make4Squares(x+offset[i].x*(widthLocal/4), y+offset[i].y*(heightLocal/4), 
            // make4Squares(x, y, 
            widthLocal/2, heightLocal/2, 
            depthMap, 
            depth+1 );
          done=true;
        } else if (!done) {
          // yes 
          // generate square  
          // set color 
          color myColor = color(random(0, 255), random(0, 255), random(0, 255));
          // color myColor = color(0, 0, 255); // blue
          // generate random time after which to start our animation 
          int waitTime=int( random (400, 3000));
          Square newSquare = new Square(x+offset[i].x*(widthLocal/4), y+offset[i].y*(heightLocal/4), 
            widthLocal/2, heightLocal/2, 
            myColor, waitTime);
          // add it to list 
          squares.add (newSquare);
        } // else
      } // for
    } // func 
    
    // -------------------------------------------------------
    // help funcs for draw()
    
    void whiteBackgroundBar() {
      noStroke();
      fill(255);
      rectMode(CORNER); 
      rect(0, borderY, 
        6*RECT_WIDTH2+2, 2*RECT_HEIGHT2+1);
    }
    
    boolean allAreFinished() {
      // returns whether growing for all rects has finished 
      for (Square s : squares ) { 
        if (!s.finished)
          return false; // no
      } // for 
      return true; // yes
    } // func 
    
    // ========================================================
    
    class Square {
    
      // class contains all of the information you need to draw a square - 
      // its position and size. 
      // Then it'll be easier to substitute a square's "children" in.
    
      // pos
      float x=0;
      float y=0;
    
      // size
      float w=0;
      float h=0;
    
      // for grow: the max size (growing ends here)
      float wMax;
      float hMax; 
    
      // color 
      color RectClassColor=0; 
    
      // time: growing doesn't start at once
      int startTime=millis(); 
      int waitTime; 
    
      // mark has growing has ended
      boolean finished = false; 
    
      // constr 
      Square(float tempX, float tempY, 
        float tempWMax, float tempHMax, 
        color tempRectClassColor, 
        int tempWaitTime) {
    
        x = tempX;  // pos 
        y = tempY;
    
        wMax=tempWMax;  // max size 
        hMax=tempHMax; 
    
        waitTime = tempWaitTime; // time until the start 
    
        RectClassColor = tempRectClassColor;
        //
      } // constr
    
      void display() {    
        // after a certain time, they are displayed
        if (millis() - startTime >= waitTime) {
          stroke(RectClassColor);
          noFill(); 
          rectMode(CENTER);  // Set rectMode to CENTER
          rect (x, y, w, h);
        }
      } // method 
    
      //void setXY(float x_, float y_) {
      //  x=x_;
      //  y=y_;
      //}
    
      void incrementWH() {
        // grow. 
        // after a certain time, they start to grow 
        if (millis() - startTime >= waitTime) {
          // growing (until max is reached)
          if (w<wMax)
            w+=1;
          else 
          finished=true; // mark as finished 
          if (h<hMax)
            h+=1;
          else 
          finished=true; // mark as finished
        } // if
      } // method 
      //
    } // class
    // ======================================
    
  • this is my approach

    so recursion is not in the class but in a function make4Squares that sets up the ArrayList with the rects ...

    Best, Chrisir ;-)

  • Wow. :O Thanks! I'll go take a look and see what I can do with this, It looks great!

  • Basically make4Squares calls itself a few times, each time dividing one square into 4 smaller ones

    after a limit, it ends calling itself and draws

    How many times it calls itself, is called depth

    Every time it calls itself the rects get smaller

    It's called recursion, maybe you look it up in wikipedia

Sign In or Register to comment.