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:
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
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 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;
}
}
// 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
// ======================================
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
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.
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
ofSquare
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!
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:
Below is the same code except that the width is double the height and so I make two Box instances:
If you have questions I will be happy to answer
Wow, thank you! I'm going to take a good look at it! Thank you so mich for your time!
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
I will ;)