【Sutcliffe Pentagons】奇幻派的漂流

我要做什么?

Sutcliffe Pentagons!一个奇异的分形怪咖——

绘制一个普通的五边形,然后找出其五边中每一边的中点并过这些点绘制垂直于此边的线段,而这些线段的末端则连接生成另一个五边形。我打个草图:
这里写图片描述

在这样做的同时,该形状的其余部分也最终被细分为更多的五边形。这意味着在每个五边形内有六个副五边形。在其中的每一个中,又有六个子五边形向下重复……

接着,怪咖出现了。


###我怎么做的?
Do you find yourself overly thrilled by the mathematics of this?

打开你的草图—— Processing, 开工吧!!!
####Step 1: Drawing a pentagon using rotation
画一个普通的五边形,你会怎么做?

我选用的是一种较通用的方式:

围绕屏幕中心旋转360度,并以特定角度推算顶点。例如,如果每72度绘制一个点,那我们就会得到一个五角形。

// 根类
class Root {
  PVector[] pointArr;  // 顶点的数组
  Branch rootBranch;

  Root() {
    pointArr = new PVector[sideNum]; // 五边形时,sideNum=5
    int count = 0;
    for (float i = 0; i<360; i+=360/sideNum) {
      float x = width/2 + (400 * cos(radians(i)));
      float y = height/2 + (400 * sin(radians(i)));
      if (count < sideNum) {  // 保证数组长度
        pointArr[count] = new PVector(x, y);
      }
      count++;
    }
    rootBranch = new Branch(0, pointArr);
  }

  void display() {
    stroke(strokeHue, 70, 100, 100);
    rootBranch.display();
  }
}

####Step 2: Functions to calculate the midpoints of a set of vertices
根发芽长成了枝丫。

我们需要一个枝丫类。枝丫从根那里得到生长的消息,它开始猥琐发育了。

在这里,我们会接收五边形每个顶点的坐标,用以求得中点。

// 枝丫类
class Branch {
  int level, num;
  PVector[] outerPoints = {};
  PVector[] midPoints = {};
  PVector[] projPoints = {};
  Branch[] myBranches = {};

  Branch(int lev, PVector[] points) {
    level = lev;
    outerPoints = points;
    midPoints = calcMidPoints();
    projPoints = calcStrutPoints();

    if ((level+1) < maxLevels) {
      Branch childBranch = new Branch(level+1, projPoints);
      myBranches = (Branch[])append(myBranches, childBranch);

      for (int k = 0; k < outerPoints.length; k++) {
        int nextk = k-1;
        if (nextk < 0) { 
          nextk += outerPoints.length;
        }
        PVector[] newPoints = { projPoints[k], midPoints[k], 
          outerPoints[k], midPoints[nextk], projPoints[nextk] };
        childBranch = new Branch(level+1, newPoints);
        myBranches = (Branch[])append(myBranches, childBranch);
      }
    }
  }

  // 得到每边中点的数组
  PVector[] calcMidPoints() {
    PVector[] mpArray = new PVector[outerPoints.length];
    for (int i = 0; i < outerPoints.length; i++) {
      int nexti = (i+1)%outerPoints.length;
      PVector thisMP = calcMidPoint(outerPoints[i], outerPoints[nexti]);
      mpArray[i] = thisMP;
    }
    return mpArray;
  }

  // 计算每边的中点
  PVector calcMidPoint(PVector end1, PVector end2) {
    return new PVector((end1.x + end2.x)/2, (end1.y + end2.y)/2);
  }
  
  void display() {
    strokeWeight(5-level);
    // 绘制外界形状
    for (int i = 0; i < outerPoints.length; i++) {        
      //int nexti = i+1;
      //if (nexti == outerPoints.length) {  // 首尾相连
      //  nexti = 0;
      //}
      int nexti = (i+1)%outerPoints.length;  // 首尾相连

      // 连接相邻两个点
      line(outerPoints[i].x, outerPoints[i].y, outerPoints[nexti].x, outerPoints[nexti].y);
    }

    // 绘制并连接中点与对应的枝点
    //for (int j = 0; j < midPoints.length; j++) {
    //line(midPoints[j].x, midPoints[j].y, projPoints[j].x, projPoints[j].y);
    //ellipse(midPoints[j].x, midPoints[j].y, 15, 15);
    //ellipse(projPoints[j].x, projPoints[j].y, 15, 15);
    //}

    // 绘制子枝
    for (int k = 0; k < myBranches.length; k++) {
      myBranches[k].display();
    }
  }
}

####Step 3: Functions to extend the midpoints toward the opposite points
毫无疑问,我们必须给定枝丫能够延伸的长度。

这时候,我定义了一个全局变量—— strutFactor 来指定希望的枝丫跨度的比例:

float strutFactor=0.2;  // 生长常数

你需要的是从中点延伸出来支柱末端的坐标集。可能你会想到利用其相对于侧顶点的角度来完成这件事。我觉得我们可以改为使用中点相对的顶点来定位。

我们将下面的两个方法加入到 Branch 类(枝丫类)。

 // 得到每个中点对应的枝点的数组
  PVector[] calcStrutPoints() {
    PVector[] strutArray = new PVector[midPoints.length];
    for (int i=0; i<midPoints.length; i++) {
      int nexti = (i+3)%midPoints.length;  // 每一边中点相对的顶点
      PVector thisSP = calcProjPoint(midPoints[i], outerPoints[nexti]);
      strutArray[i] = thisSP;
    }
    return strutArray;
  }

  // 计算中点对应的枝点
  PVector calcProjPoint(PVector mp, PVector op) {
    float px, py;
    px = mp.x + (op.x - mp.x) * strutFactor;
    py = mp.y + (op.y - mp.y) * strutFactor;
    return new PVector(px, py);
  }

为了运行方便,真正起到递归作用的代码,我早已加入到 Branch 类。

if ((level+1) < maxLevels) {
      Branch childBranch = new Branch(level+1, projPoints);
      myBranches = (Branch[])append(myBranches, childBranch);

      for (int k = 0; k < outerPoints.length; k++) {
        int nextk = k-1;
        if (nextk < 0) { 
          nextk += outerPoints.length;
        }
        PVector[] newPoints = { projPoints[k], midPoints[k], 
          outerPoints[k], midPoints[nextk], projPoints[nextk] };
        childBranch = new Branch(level+1, newPoints);
        myBranches = (Branch[])append(myBranches, childBranch);
      }
    }

根生枝,枝生根。
这里写图片描述

####Step 4: Varying the strut length of a Sutcliffe Pentagon
主标签实现了图形的绘制与变幻。strutFactor 的值范围在 - 0.5~1.0。

int sideNum = 5;  // 边数
float strutFactor=0.2;  // 生长常数
int maxLevels = 5;
float strutNoise;
Root pentagon;

void setup() {
  fullScreen();
  smooth();
  strutNoise = random(10);
  pentagon = new Root();
}

void draw() {
  background(255);
  strutNoise += 0.01;
  strutFactor = ((noise(strutNoise) * 3) - 1)/2;
  pentagon.display();
}


###我学到了什么?
不是所有现实事物都受益于某种结构化的规则;有一点混沌漂移才会更好。如果你有一种允许这种自由的工作方式,能够通过灵感把一种想法变成另一种想法,那么你将不可避免地踏足一些更加有趣的领域。

Recursive fractal constructions are at their root an idea stolen from natural organization. Despite how it may look when viewed through a screen, we don’t live in a digital world. Our reality is stubbornly analog: it doesn’t fit into distinctly encodable states. It’s an intricate, gnarly, and indefinite place. If we want to use digital tools to create art, which somehow has to reflect our crazy, chaotic existence, we need to allow imperfection and unpredictability into the equation. But, as I hope I’ve shown, there is room for the organic within the mechanical, and programming isn’t just about efficiency and order. Our computing machines, which may only be capable of producing poor imitations of life, are tapping a computational universality that the natural world shares.

####Last…
附上一张【Sutcliffe Pentagons】壁纸。
这里写图片描述
####相关资源:

posted @ 2020-01-12 10:00  升卿  阅读(99)  评论(0)    收藏  举报