Processing | 用递归创造复杂图形

  版权信息:
● 本博客使用CC 3.0协议,转载请保留该信息。
● 原文作者: 戴晓天 @ 云飞机器人实验室
● 原文地址: Processing | 用递归创造复杂图形

在Processing中想要创作复杂的图形元素,除了可以使用循环结构外,还可以使用递归 (recursion) 。递归是一种在函数执行时调用自身的一种特殊设计方法,递归可以解决很多循环结构无法解决的问题,同时在代码实现上也极为简洁。

为了可以更直观的了解什么是递归,这里先给出一个例子:

// Recursive Circle
// YunFei
void setup() {
  size(420, 420);
  drawCircle(400);
}

void draw() {
  noLoop();
}

void drawCircle(float radius) {

  if (radius < 10) {
    return;
  }

  ellipse(width/2, height/2, radius, radius);

  drawCircle(radius - 20);
}

该代码生成的图形如下:
20150312224944

在上例中,drawCircle() 函数在返回前再次调用了drawCircle(),同时半径参数减小了20。所以drawCircle(400)会调用drawCircle(380),drawCircle(380)绘制完毕后又会调用drawCircle(360),… …,直到drawCircle(20)调用drawCircle(0)。此时,因为radius小于给定的容限(radius < 10),drawCircle(0)直接执行了return语句返回了该函数,从而不会再继续调用drawCircle(-20)。随着drawCircle(0)的返回,其余函数也逐级返回,直到所有嵌套的函数完全结束。

递归虽然相比循环在代码上更为简洁,但是设计难度却大于循环结构。设计递归时有两个注意点:
1)是程序的规模要不断减小 (此处每次radius – 20),否则程序会陷入死循环无法退出;
2)有一个合适的中止条件 (此处为radius < 10),一般来说小于某个容限值比等于某个极限值更为可靠。

下面以一个更复杂的例子,树型分形,来展现递归的美妙(代码修改于Daniel Shiffman, The Nature of Code):

// Recursive Tree
// YunFei
void setup() {
  size(500, 500);
  background(255);
  translate(width/2, height);
  stroke(0);
  drawBranch(150);
}

void draw() {
  noLoop();
}

void drawBranch(float len) {
  float theta = PI/6;

  strokeWeight(2);
  line(0, 0, 0, -len);

  translate(0, -len);

  len *= 0.66;

  if (len > 10) {
    pushMatrix();
    rotate(theta);
    drawBranch(len);
    popMatrix();    

    pushMatrix();
    rotate(-theta);
    drawBranch(len);
    popMatrix();
  }
}

实现的效果如下:
20150312232827

以上分形图形随着枝的逐层生长,分支的数量呈指数倍、而非线性增长,循环结构无法解决该问题。可见,灵活使用递归可以创造更为复杂的图形元素。实际上,递归更多情况下被用于求解数学问题,如最为经典的牛顿迭代法求解方程,就可以使用递归函数来计算。

%d bloggers like this: