Understanding recursive Koch Snowflake function in Postscript
Asked Answered
P

3

2

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:

  1. What does 2 copy ge { dup 0 rlineto } mean here?
  2. How does ifelse work here and what is the condition?
  3. What does 3 div do here?
  4. What does the 2 copy KochR statement perform here?
Pearl answered 12/9, 2012 at 5:46 Comment(2)
There is one KochR, whereas all the rest are kochRs. Did you copy the code all correctly?Somerset
I tested your code and there is indeed a bug in the code. "2 copy KochR" should be "2 copy kochR" (see K vs. k). - edited it.Shantae
A
1

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 ?

  1. 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.

  2. 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. 3 div divides the operand on the stack by 3 and places the result on the operand stack.

  4. 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.

Andino answered 12/9, 2012 at 7:30 Comment(3)
From above program prespective what "2 copy ge {dup 0 rlineto} " is doing? Actually I am trying to convert this program to C? I am not a regular post script programmer. What 2 stands here?Pearl
In case anyoune else is just curious, first the upcase K is an error, should be all lower case. The after fixing that code draws a Koch curve fractal patern (pretty cool for such a small bit of code..). I bet google-ing "kotch curve in C" would be more efficient than learning postscript.Rutkowski
@venkysmarty: Please!! You asked the question already at Copy command in PostScript. You can at least be expected to read the answers over there. Then you would at least know that 2 copy duplicates the two topmost items that are on the stack already and places them (one more time) onto the stack. -- Also it would be nice if you upvoted responses which are correctly answering your question.Somerset
T
1
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. :)

Territoriality answered 12/9, 2012 at 16:3 Comment(0)
A
1

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).

Airplane answered 13/9, 2012 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.