Processing: サトクリフ五角形サンプルのリファクタリング

マット・ピアソン著『ジェネラティブ・アート Processingによる実践ガイド』「Chapter 8 フラクタル」に出てくる「サトクリフ五角形」のサンプルコードがどうも冗長なのでリファクタリングしてみた。特に、数学的に無意味な条件分岐を整理したら随分すっきりした。
f:id:soutoku:20140825101358j:plain
サトクリフ五角形

int _maxlevels = 5;
float _strutFactor = 0.2;

void setup() {
  size(800, 800); //オリジナルはsize(1000, 1000);
  smooth();
  FractalRoot pentagon = new FractalRoot();
  pentagon.drawShape();
}

class PointObj {
  float x, y;
  
  PointObj(float ex, float ey) {
    x = ex;
    y = ey;
  }
}

class FractalRoot {
  Branch rootBranch;
  
  FractalRoot() {
    float centerX = width/2;
    float centerY = height/2;
    PointObj[] points = new PointObj[5];
    int radius = 400;
    int apex = 0;
    for (int angle = 0; angle < 360; angle += 72) {
      float x = centerX + (radius * cos(radians(angle)));
      float y = centerY + (radius * sin(radians(angle)));
      points[apex] = new PointObj(x, y);
      apex++;
    }
    rootBranch = new Branch(0, 0, points);
  }
  
   void drawShape() {
     rootBranch.drawMe();
   }
}

class Branch {
  int level, num;
  PointObj[] outerPoints = {};
  PointObj[] midPoints = {};
  PointObj[] projPoints = {};
  Branch[] myBranches = {};
   
  Branch(int lev, int n, PointObj[] points) {
    level = lev;
    num = n;
    outerPoints = points;
    midPoints = calcMidPoints();
    projPoints = calcStrutPoints();
    
    if ((level + 1) < _maxlevels) {
      Branch childBranch = new Branch(level + 1, 0, projPoints);
      myBranches = (Branch[])append(myBranches, childBranch);
      
      for (int apex = 0; apex < outerPoints.length; apex++) {
        int nextApex = apex - 1;
        if (nextApex < 0) {
          nextApex += outerPoints.length;
        }
        PointObj[] newPoints = { projPoints[apex], midPoints[apex],
                              outerPoints[apex], midPoints[nextApex],
                              projPoints[nextApex] };
        childBranch = new Branch(level + 1, apex + 1, newPoints);
        myBranches = (Branch[])append(myBranches, childBranch);
      }
    }
  }
    
  void drawMe() {
    strokeWeight(5 - level);
    drawOuter();
    strokeWeight(0.5);
    drawMid();
    drawBranch();
  }

  void drawOuter() {
    for (int apex = 0; apex < outerPoints.length; apex++) {
      int nextApex = apex + 1;
      if (nextApex == outerPoints.length) {
        nextApex = 0;
      }
      line(outerPoints[apex].x, outerPoints[apex].y,
           outerPoints[nextApex].x, outerPoints[nextApex].y);
    }
  }
  
  void drawMid() {
    for (int apex = 0; apex < midPoints.length; apex++) {
      line(midPoints[apex].x, midPoints[apex].y,
           projPoints[apex].x, projPoints[apex].y);
    }
  }

  void drawBranch() {
    for (int apex = 0; apex < myBranches.length; apex++) {
      myBranches[apex].drawMe();
    }
  }
  
  PointObj[] calcMidPoints() {
    PointObj[] points = new PointObj[outerPoints.length];
    for (int apex = 0; apex < outerPoints.length; apex++) {
      int nextApex = apex + 1;
      if (nextApex == outerPoints.length) {
        nextApex = 0;
      }
      PointObj midPoint = calcMidPoint(outerPoints[apex],
                                       outerPoints[nextApex]);
      points[apex] = midPoint;
    }
    return points;
  }
      
  PointObj calcMidPoint(PointObj point1, PointObj point2) {
    float mx, my;
    mx = calcMid(point1.x, point2.x);
    my = calcMid(point1.y, point2.y);
    return new PointObj(mx, my);
  }
  
  float calcMid(float a, float b) {
    return a + ((b - a) / 2);
  }
  
  PointObj[] calcStrutPoints() {
    PointObj[] points = new PointObj[midPoints.length];
    for (int point = 0; point < midPoints.length; point++) {
      int nextPoint = point + 3;
      if (nextPoint >= midPoints.length) {
        nextPoint -= midPoints.length;
      }
      PointObj strPoint = calcProjPoint(midPoints[point],
                                        outerPoints[nextPoint]);
      points[point] = strPoint;
    }
    return points;
  }
  
  PointObj calcProjPoint(PointObj mp, PointObj op) {
    float px, py;
    px = mp.x - ((mp.x - op.x) * _strutFactor);
    py = mp.y - ((mp.y - op.y) * _strutFactor);
    return new PointObj(px, py);
  }
}