What does 2 copy ge { dup 0 rlineto } mean here?
As the THEN clause of an if
or ifelse
operator, it means "if (stack(top-1) > stack(top)) draw_horizontal_line((current_x, current_y) -> (current_x + stack(top), current_y). The procedure-body { dup 0 rlineto }
is the closure of the recursion: the part the decides when to stop and what to do instead of recursing. rlineto
draws a relative line. Relative to the currentpoint, that is. The currentpoint is whereever the last path-construction operator (like moveto
, lineto
, but not rotate
, and not stroke
) left it.
How does ifelse work here and what is the condition?
ifelse
always works the same way: booleantype procedure-body procedure-body ifelse: execute the first procedure body if the boolean is true, otherwise execute the second. The condition is the boolean-valued result of the gt
operator applied to 2 numbers on the stack. Since gt
consumes its arguments, prepending 2 copy
means the data is not lost when gt
does this.
What does 3 div do here?
Since the second argument (top-of-stack) controls the overall size of the figure, it also controls the "size" of the partial figure represented by the combined drawing commands of all child calls. 3 div
means that at each recursion level, the "size" of the figure is smaller than the "size" of its parent, 1/3 smaller. At the leaf calls, where the condition a >= b holds, b is used as the length of the individual line-segments that compose the image. This means that a is not the line length per se, but a threshold value. As soon as b, in its descent to b/3, b/9, b/27, b/81, meets or crosses the threshold a, then its time to turn-off the cloning machine and have everybody pick up their pencils.
What does the 2 copy KochR statement perform here?
This line performs the recursive call to the kochR procedure, using the same threshold and a reduced size from two arguments that were passed to the current invocation. Using 2 copy
means the two values persist on the stack until the pop pop
further down.
Here's a rough translation to C, assuming an available graphics library that implements the Adobe Image Model (also called Stencil-Mask, or Path-Paint model). The parameters appear to be the size of the line segments and the overall size of the figure, respectively. So the maximum recursion-level is indirectly controlled by the equation a >= b * (1/3)^R.
void kochR(double a, double b) {
if (a >= b) {
// line from currentpoint to currentpoint+(b,0)
// ie. line of length b along current x-axis
rlineto(b, 0);
} else {
b /= 3;
kochR(a, b); // recurse with reduced length
rotate(60); // rotate x-axis CCW by 60 degrees
kochR(a, b);
rotate(-120); // rotate x-axis CW by 120 degrees
kochR(a, b);
rotate(60);
kochR(a, b);
}
}
int main(void) {
moveto(0,0);
kochR(27, 81);
moveto(0, 27);
kochR(9, 81);
moveto(0, 54);
kochR(3, 81);
moveto(0, 81);
kochR(1, 81);
stroke();
}
So you should be able to see from this that all the 2 copy
stuff is a means postscript has to "keep alive" 2 unnamed variables. Each line corresponds to a procedure call which consumes 2 variables from the stack. You should be able to see that the final pop pop
would be unnecessary if you also omitted the final 2 copy
from the last "procedure call". From the perspective of postscript programming this is all quite basic stuff. But the way the recursion is bounded is quite sophisticated.
By the way, if you like these kinds of fractals (I do), you absolutely must see http://en.wikipedia.org/wiki/L-system . It's amazing.
One popular C library that implements the Adobe Image Model is Cairo Graphics, but I'll leave the task of creating a working program as an exercise for the reader. :)
KochR
, whereas all the rest arekochR
s. Did you copy the code all correctly? – Somerset