Category Archives: Processing for Generative Art

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

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

Processing | The Tree of Mind

九月底我的硕士课程就正式开始了,学业一直比较繁忙,没有精力进行新的研究。近日略有饶兴,抽空重新温习了一下Processing,下一步准备用Processing和Kinect完成一些交互艺术设计。

这个作品历时两天,用了800个粒子做随机运动动态生成。粒子的运动规律是由固定的方向矢量和柏林随机噪声叠加而成的。固定的方向矢量是为了能在自由运动的同时,保证运动的大体角度,从而从总体上呈现出绽放的效果;而之所以选用柏林噪声是因为这种随机运动更加自然,不会产生位置上的突变和跳动。

processing_mindtree

Revision History >>

04/19/2016    因为视频时间较短,将视频做成了gif,方便预览。