IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Processing | 用递归创造复杂图形

    automatic.dai发表于 2015-03-12 23:05:35
    love 0

    在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

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



沪ICP备19023445号-2号
友情链接