I have followiong program in PostScript which I am having difficutly in understanding.
/kochR
{
2 copy ge {dup 0 rlineto}
{
3 div
2 copy kochR
60 rotate
2 copy kochR
-120 rotate
2 copy kochR
60 rotate
2 copy kochR
} ifelse
pop pop
} def
0 0 moveto
27 81 kochR
0 27 moveto
9 81 kochR
0 54 moveto
3 81 kochR
0 81 moveto
1 81 kochR
stroke
My questions on above program are:
- What does
2 copy ge { dup 0 rlineto }
mean here? - How does
ifelse
work here and what is the condition? - What does
3 div
do here? - What does the
2 copy KochR
statement perform here?
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. :)
The 2 copy ge
copies KochR
's 2 arguments (I am assuming it is a coordinate pair) and compares its components, getting a truth value. The ifelse
then decides, based on that truth value, whether to do { dup 0 rlineto }
or the statements in the other block. 3 div
divides the y-value of the coord pair in three, making it that point that much closer to the origin along that axis. 2 copy KochR
creates a copy of the coordinate pair (which have either had y cut in thirds, or its location rotated), which are then used recursively to perform the same thing on them.
My best estimate of what the function does is that it draws an atenuating zig-zag from the current point in the direction of the point passed to KochR
, leaving the list of pointson the operand stack. The program appends several such zig-zags to the current path, each in is own subpath, and then strokes them all, leaving the entire list of points on the stack (a possible problem).
You seem to be having some problems with quite basic PostScript concepts and operations, do you have a copy of the PostScript Language Reference Manual ?
copy copies an entry on the operand stack, the preceding argument tells the interpreter how many operands on the stack to copy. ge tests for greater than or equal and that is then followed by an executable array.
ifelse works in the way you would expect, if the condition is true then the first executable array is executed, else the second executable array is executed.
3 div divides the operand on the stack by 3 and places the result on the operand stack.
2 copy does the same as in point 1, 'KochR' is a named object, in this case it is an executable array. It must have been previously defined or an 'undefined'' error will occur.
来源:https://stackoverflow.com/questions/12381887/understanding-recursive-koch-snowflake-function-in-postscript