Dear all,
I would really appreciate if someone could help me decipher some parts of Jared Tarbell’s “substrate” algorithm.
For those who are not familiar with this famous sketch, here is a recap in Tarbell’s own words:
A single line (known internally as a “crack”) begins drawing itself from some random point in some random direction. The line continues to draw itself until it either (a) hits the edge of the screen or (b) hits another line, at which point it stops and two more lines begin. The one simple rule used in the creation of new lines is that they begin at tangents to existing lines. This process is repeated until there are too many lines to keep track of or the program is stopped.
For clarity I have boiled down the original script to its essence:
int dimx = 250;
int dimy = 250;
int num = 0;
int maxnum = 100;
// grid of cracks
int[] cgrid;
Crack[] cracks;
// MAIN METHODS ---------------------------------------------
void setup() {
size(250,250,P3D);
// size(dimx,dimy,P3D);
background(255);
cgrid = new int[dimx*dimy];
cracks = new Crack[maxnum];
begin();
}
void draw() {
// crack all cracks
for (int n=0;n<num;n++) {
cracks[n].move();
}
}
void mousePressed() {
begin();
}
// METHODS --------------------------------------------------
void makeCrack() {
if (num<maxnum) {
// make a new crack instance
cracks[num] = new Crack();
num++;
}
}
void begin() {
// erase crack grid
for (int y=0;y<dimy;y++) {
for (int x=0;x<dimx;x++) {
cgrid[y*dimx+x] = 10001;
}
}
// make random crack seeds
for (int k=0;k<16;k++) {
int i = int(random(dimx*dimy-1));
cgrid[i]=int(random(360));
}
// make just three cracks
num=0;
for (int k=0;k<3;k++) {
makeCrack();
}
background(255);
}
// OBJECTS -------------------------------------------------------
class Crack {
float x, y;
float t; // direction of travel in degrees
Crack() {
// find placement along existing crack
findStart();
}
void findStart() {
// pick random point
int px=0;
int py=0;
// shift until crack is found
boolean found=false;
int timeout = 0;
while ((!found) || (timeout++>1000)) {
px = int(random(dimx));
py = int(random(dimy));
if (cgrid[py*dimx+px]<10000) {
found=true;
}
}
if (found) {
// start crack
int a = cgrid[py*dimx+px];
if (random(100)<50) {
a-=90+int(random(-2,2.1));
} else {
a+=90+int(random(-2,2.1));
}
startCrack(px,py,a);
} else {
//println("timeout: "+timeout);
}
}
void startCrack(int X, int Y, int T) {
x=X;
y=Y;
t=T;//%360;
x+=0.61*cos(t*PI/180);
y+=0.61*sin(t*PI/180);
}
void move() {
// continue cracking
x+=0.42*cos(t*PI/180);
y+=0.42*sin(t*PI/180);
// bound check
float z = 0.33;
int cx = int(x+random(-z,z)); // add fuzz
int cy = int(y+random(-z,z));
// draw black crack
stroke(0,85);
point(x+random(-z,z),y+random(-z,z));
if ((cx>=0) && (cx<dimx) && (cy>=0) && (cy<dimy)) {
// safe to check
if ((cgrid[cy*dimx+cx]>10000) || (abs(cgrid[cy*dimx+cx]-t)<5)) {
// continue cracking
cgrid[cy*dimx+cx]=int(t);
} else if (abs(cgrid[cy*dimx+cx]-t)>2) {
// crack encountered (not self), stop cracking
findStart();
makeCrack();
}
} else {
// out of bounds, stop cracking
findStart();
makeCrack();
}
}
}
What I do not understand is how the line detection mechanism work.
All seem to happen in the move()
function within the Crack()
class after the first if
statement:
if ((cx>=0) && (cx<dimx) && (cy>=0) && (cy<dimy)) {
// safe to check
if ((cgrid[cy*dimx+cx]>10000) || (abs(cgrid[cy*dimx+cx]-t)<5)) {
// continue cracking
cgrid[cy*dimx+cx]=int(t);
} else if (abs(cgrid[cy*dimx+cx]-t)>2) {
// crack encountered (not self), stop cracking
findStart();
makeCrack();
}
} else {
// out of bounds, stop cracking
findStart();
makeCrack();
}
}
What I understand:
- here
cgrid
is an array list of sizewidth * height
containing integers - at the start the
begin()
function set all these integers to10001
and then randomly replaces 16 of them by a random angle. cx
andcy
are the x and y coordinates of a moving point (drawing the crack)cy*dimx+cx
is an index (similar to thex + y * width
we usually use for pixel indexing)t
is the angle previously chosen at random
consequently cgrid[cy*dimx+cx]
corresponds to a specific element in the array list. That element should be an integer either equal to 10001 or between 0 and 360 (the angle)
What I do NOT understand:
- pretty much the whole statement and the different parts it is related to
- Why
10001
specifically ? - If that element in the array list is equal to 10001 (or >10000), why should it be then equal to
t
(the angle) ? - What
(abs(cgrid[cy*dimx+cx]-t)<5))
corresponds to ? why<5
? - What
abs(cgrid[cy*dimx+cx]-t)
corresponds to ? What does “angle in the list minus angle” mean here ?
And the thing is I know these very statements make all the lines stop when hitting an edge or an other line but I just can’t grasp the logic behind it… it drives me crazy.
Hope someone clever than me can help me understand all this.