了解Postscript中的递归Koch Snowflake函数

时间:2012-09-12 05:46:38

标签: recursion postscript fractals

我在PostScript中有跟随程序,我在理解方面有困难。

/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

我对上述计划的疑问是:

  1. 2 copy ge { dup 0 rlineto }在这里意味着什么?
  2. ifelse如何在这里工作?条件是什么?
  3. 3 div在这做什么?
  4. 2 copy KochR声明在此处执行了什么?

3 个答案:

答案 0 :(得分:1)

您似乎在使用非常基本的PostScript概念和操作时遇到了一些问题,您是否有PostScript语言参考手册的副本?

  1. copy复制操作数堆栈上的一个条目,前面的参数告诉解释器要复制的堆栈上有多少个操作数。 ge测试大于或等于,然后是可执行数组。

  2. ifelse以您期望的方式工作,如果条件为真,则执行第一个可执行数组,否则执行第二个可执行数组。

  3. 3 div将堆栈上的操作数除以3并将结果放在操作数堆栈上。

  4. 2副本与第1点相同,'KochR'是一个命名对象,在这种情况下它是一个可执行数组。它必须先前已定义,否则将出现“未定义”错误。

答案 1 :(得分:1)

What does 2 copy ge { dup 0 rlineto } mean here?

作为ififelse运算符的THEN子句,它表示“if(stack(top-1)> stack(top))draw_horizo​​ntal_line((current_x,current_y) - > (current_x + stack(top),current_y)。procedure-body { dup 0 rlineto }是递归的闭包:决定何时停止以及做什么而不是递归的部分。{ {1}}绘制相对行。相对于当前点,即。当前点是最后一个路径构造运算符(如rlineto,{ {1}},但不是moveto,而不是lineto)离开了它。

rotate

stroke总是以相同的方式工作:booleantype procedure-body procedure-body ifelse:如果boolean为true,则执行第一个过程体,否则执行第二个过程体。条件是应用于堆栈上2个数字的How does ifelse work here and what is the condition? 运算符的布尔值结果。由于ifelse会使用其参数,因此预先gt表示gt执行此操作时数据不会丢失。

2 copy

由于第二个参数(堆栈顶部)控制图形的整体大小,它还控制由所有子调用的组合绘图命令表示的局部图形的“大小”。 gt表示在每个递归级别,图的“大小”小于其父级的“大小”,小1/3。在叶子调用中,在条件a>> = b的情况下,b被用作组成图像的各个线段的长度。这意味着a不是行长本身,而是阈值。 b,在下降到b / 3,b / 9,b / 27,b / 81时,遇到或超过阈值a,然后是时候关闭克隆机并让每个人拿起他们的铅笔。

What does 3 div do here?

该行执行对kochR过程的递归调用,使用相同的阈值和从传递给当前调用的两个参数减小的大小。使用3 div表示这两个值在堆栈中保持不变,直到What does the 2 copy KochR statement perform here? 进一步向下。


这是对C的粗略转换,假设有一个可用的图形库来实现Adobe Image Model(也称为Stencil-Mask或Path-Paint模型)。参数看起来分别是线段的大小和图的整体大小。因此,最大递归水平由等式a> = b *(1/3)^ R间接控制。

2 copy

所以你应该能够从中看到所有pop pop东西都是后记必须“保持活跃”2个未命名变量的手段。每行对应一个过程调用,它从堆栈中消耗2个变量。如果您还省略了上一次“过程调用”中的最终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(); } ,您应该能够看到最终的2 copy是不必要的。从 postscript编程的角度来看,这都是非常基本的东西。但递归受限的方式非常复杂。

顺便说一下,如果你喜欢这些分形(我这样做),你绝对必须看到http://en.wikipedia.org/wiki/L-system。太棒了。

实现Adobe Image Model的一个流行的C库是Cairo Graphics,但我将创建一个工作程序作为读者的练习。 :)

答案 2 :(得分:1)

2 copy ge副本KochR的2个参数(我假设它是一个坐标对)并比较其组件,获得真值。然后ifelse根据该真值确定是{ dup 0 rlineto }还是其他块中的语句。 3 div将coord对的y值除以三,使得该点更接近沿该轴的原点。 2 copy KochR创建一个坐标对的副本(其中y可以在三分之一处切割,或者其位置旋转),然后递归使用它们对它们执行相同的操作。

我对该函数的最佳估计是它从传递给KochR的点的方向上的当前点绘制了一个减弱的曲折,留下了操作数堆栈中的点列表。该程序将几个这样的zig-zags附加到当前路径,每个zig-zags都是自己的子路径,然后将它们全部划分,将整个点列表留在堆栈上(可能存在问题)。