Click here to Skip to main content
11,584,556 members (73,812 online)
Click here to Skip to main content

Tagged as

Generative Art - Case Study: Sutcliffe Pentagons

, 21 Jun 2011 CPOL 16.8K 24
Rate this:
Please Sign up or sign in to vote.
A chapter excerpt from Generative Art
image002.jpg Generative Art
A practical guide using Processing

Matt Pearson

If you draw a pentagon, then plot the midpoints of each of its five sides and extend a line perpendicular to each of these points, you can connect the end of these lines to make another pentagon. In doing this, the remaining area of the shape also ends up being subdivided into further pentagons, meaning that within each pentagon are six subpentagons. And within each of those, another six sub-pentagons, repeated downward toward the infinitesimal. In this article, based on chapter 8 of Generative Art, author Matt Pearson explains how exactly to do that.

To save 35% on your next purchase use Promotional Code pearson0835 when you check out at www.manning.com.

You may also be interested in…

In 2008 I went to a meeting of the Computer Arts Society (CAS) in London, an organization that was (remarkably, for a body devoted to computing) celebrating its fortieth anniversary with that event. There, I heard a short talk by the society’s initiator and first chairman, the artist and mathematician Alan Sutcliffe.[1] Through this talk, he introduced me to a marvelous shape, which I have since mentally dubbed the Sutcliffe Pentagon.

To be clear, I’ve spoken to Alan about this, and he isn’t entirely over the moon with me using this name. He has been insistent that, even though he thought it up, he doesn’t believe the shape was his invention. He cites Kenneth Stephenson ’s work on circle packing[2] as preceding him, in which Stephenson himself credits earlier work by Floyd, Cannon, and Parry.[3] But I have since reviewed this trail of influence, and, although Sutcliffe’s predecessors do describe a dissected pentagon, none of them take the idea half as far as he did. And even if they had, it wouldn’t change the way the Sutcliffe Pentagon was burned into my brain that particular evening.

So, respecting Alan’s modesty, and his proviso that we probably shouldn’t, strictly speaking, refer to this construct as a Sutcliffe Pentagon, let me begin by showing you what a Sutcliffe Pentagon looks like (see figure 1). Then, I’ll talk you though how to construct a Sutcliffe Pentagon, and finally I’ll show you a number of different Sutcliffe Pentagon–derived works, developed using the Sutcliffe Pentagon structure.

Figure 1 Sutcliffe pentagons

Sutcliffe noticed that if you draw a pentagon, then plot the midpoints of each of its five sides and extend a line perpendicular to each of these points, you can connect the end of these lines to make another pentagon. In doing this, the remaining area of the shape also ends up being subdivided into further pentagons, meaning that within each pentagon are six subpentagons. And within each of those, another six sub-pentagons, repeated downward toward the infinitesimal.

If you don’t find yourself overly thrilled by the mathematics of this yet, bear with me. In the following section, I’ll show what you can do with this simple principle. Let’s start by coding it up; then, I’ll demonstrate some of the directions I took it in.

Construction

You begin by drawing a regular pentagon. But the way I approached it, you don’t just draw five lines between five points; instead, you create a drawing method that is a little more generic—rotating 360 degrees around a center and extrapolating points at certain angles. For example, if you plot a point every 72 degrees, you get a pentagon.

Listing 1 Drawing a pentagon using rotation

FractalRoot pentagon;
int _maxlevels = 5;

void setup() {
 size(1000, 1000);
 smooth();
 pentagon = new FractalRoot();                          #A
 pentagon.drawShape();                                  #B
}

class PointObj { 
 float x, y;
 PointObj(float ex, float why) {                        #C
  x = ex; y = why;
 }
}

class FractalRoot {
 PointObj[] pointArr = new PointObj[5];
 Branch rootBranch;

 FractalRoot() {
   float centX = width/2;
   float centY = height/2;
   int count = 0;
   for (int i = 0; i<360; i+=72) {
    float x = centX + (400 * cos(radians(i)));
    float y = centY + (400 * sin(radians(i)));
    pointArr[count] = new PointObj(x, y);
    count++;
   }
   rootBranch = new Branch(0, 0, pointArr);
 }

 void drawShape() {
  rootBranch.drawMe();
 }
}

class Branch {
 int level, num;
 PointObj[] outerPoints = {};

 Branch(int lev, int n, PointObj[] points) {            #D
  level = lev;
  num = n;
  outerPoints = points;
 }
  void drawMe() {
  strokeWeight(5 - level);
  // draw outer shape
  for (int i = 0; i < outerPoints.length; i++) {        #E
  int nexti = i+1;
  if (nexti == outerPoints.length) { nexti = 0; }
  line(outerPoints[i].x, outerPoints[i].y,
outerPoints[nexti].x, outerPoints[nexti].y);
  }
  }

}
#A Creates root pentagon
#B Calls drawShape method on it
#C Stores x,y position
#D Constructs Branch object
#E Branch draws itself

The FractalRoot object contains an array of five points. It fills that array by rotating an angle around a center point in 72-degree jumps. It then uses that array of points to construct the first Branch object. The Branch object is your self-replicating fractal object. Each branch draws itself by connecting the points it has placed in its outerPoints array.

This should give you a pentagon, as shown in figure 2.

Figure 2 Just in case you don’t know what a regular pentagon looks like

Next, you plot the midpoints of each side. Add the two functions in the following listing to the Branch object.

Listing 2 Functions to calculate the midpoints of a set of vertices

PointObj[] calcMidPoints() {
  PointObj[] mpArray = new PointObj[outerPoints.length];
  for (int i = 0; i < outerPoints.length; i++) {
   int nexti = i+1;
   if (nexti == outerPoints.length) { nexti = 0; }
   PointObj thisMP = calcMidPoint(outerPoints[i],
outerPoints[nexti]);
 mpArray[i] = thisMP;
}
return mpArray;
}

PointObj calcMidPoint(PointObj end1, PointObj end2) {
 float mx, my;
 if (end1.x > end2.x) {
   mx = end2.x + ((end1.x - end2.x)/2);
 } else {
   mx = end1.x + ((end2.x - end1.x)/2);
 }
 if (end1.y > end2.y) {
   my = end2.y + ((end1.y - end2.y)/2);
 } else {
   my = end1.y + ((end2.y - end1.y)/2);
 }
 return new PointObj(mx, my);
}

The first function, calcMidPoints, iterates through the array of outer points and passes each pair of points to the second function, calcMidPoint; this function works out the difference between each individual pair, covering every possible relative position to each other. calcMidPoints collects each of the results in an array to pass back.

You call the new function in the Branch constructor and put the result in an array named midpoints:

PointObj[] midPoints = {};

Branch(int lev, int n, PointObj[] points) {
 …
 midPoints = calcMidPoints();
}

This gives you figure 8.12.

Figure 3 Calculating the midpoints of the sides

The next set of points you need are the ends of the struts to extend from the midpoints. You don’t need to work this out relative to the angle of the side vertex; you can instead use one of the opposite points of the shape to aim toward.

First, define a global _strutFactor variable to specify the ratio of the total span you want the strut to extrude:

float _strutFactor = 0.2;

Now, the Branch object needs another set of functions to plot those points.

Listing 3 Functions to extend the midpoints toward the opposite points

PointObj[] calcStrutPoints() {
  PointObj[] strutArray = new PointObj[midPoints.length];
  for (int i = 0; i < midPoints.length; i++) {
    int nexti = i+3;
    if (nexti >= midPoints.length) { nexti -= midPoints.length; }
    PointObj thisSP = calcProjPoint(midPoints[i], outerPoints[nexti]);  #A
    strutArray[i] = thisSP;
  }
  return strutArray;
}

PointObj calcProjPoint(PointObj mp, PointObj op) {
  float px, py; float adj, opp;
  if (op.x > mp.x) {
     opp = op.x - mp.x;
  }else{
     opp = mp.x - op.x;
  }
  if (op.y > mp.y) {                                    #B
     adj = op.y - mp.y;
  }else{
     adj = mp.y - op.y;
  }
  if (op.x > mp.x) {
     px = mp.x + (opp * _strutFactor);
  } else {
     px = mp.x - (opp * _strutFactor);
  }
  if (op.y > mp.y) {                                    #C
     py = mp.y + (adj * _strutFactor);
  } else {
     py = mp.y - (adj * _strutFactor);
  }
  return new PointObj(px, py);
}
#A Projects midpoint to opposite outer point
#B Trig calculations
#C Projects along hypotenuse

Again, add this code to the Branch constructor:

PointObj[] projPoints = {};

Branch(int lev, int n, PointObj[] points) {
 ...
 projPoints = calcStrutPoints();
}

And plot those points in the drawMe function so you can see they’re correct:

strokeWeight(0.5);
fill(255, 150);
for (int j = 0; j < midPoints.length; j++) {
 ellipse(midPoints[j].x, midPoints[j].y, 15, 15);
 line(midPoints[j].x, midPoints[j].y,
          projPoints[j].x, projPoints[j].y);
 ellipse(projPoints[j].x, projPoints[j].y, 15, 15);
}

You should be looking at something like figure 4.

Figure 4 Project struts toward the opposite points

Now you have all the points you need for the recursive structure. You can test this first by passing the five strut points as the outer points for an inner pentagon. Add the following to the Branch object’s constructor:

Branch[] myBranches = {};

Branch(int lev, int n, Point[] points) {
 ...
 if ((level+1) < _maxlevels) {
  Branch childBranch = new Branch(level+1, 0, projPoints);
  myBranches = (Branch[])append(myBranches, childBranch);
 }
}

Then, add a similar trickle-down method to the drawMe function so the children are rendered to the screen:

void drawMe() {
...
  for (int k = 0; k < myBranches.length; k++) {
    myBranches[k].drawMe();
  }
}

You should see the result shown in figure 5.

Figure 5 A recursive pentagon

Adding the other five pentagons to create a complete Sutcliffe Pentagon fractal is just a matter of passing the points to five more new Branch objects. The code is as follows:

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

This gives you the fractal shown at the beginning of this section in figure 1. Now you can begin experimenting with it.

Exploration

You have the ratio of the projected strut in a handy variable, so let’s start by varying this. You’ll add a draw loop to redraw the fractal every frame with a new strut length, and vary that length by a noise factor. For this modification, you don’t need to do anything to the objects—just update the initialization section of the code as shown in the following listing.

Listing 4 Varying the strut length of a Sutcliffe Pentagon

FractalRoot pentagon;
float _strutFactor = 0.2;
float _strutNoise;
int _maxlevels = 4;                                     3A

void setup() {
 size(1000, 1000);
 smooth();
 _strutNoise = random(10);
}

void draw() {
 background(255);

 _strutNoise += 0.01;
 _strutFactor = noise(_strutNoise) * 2;

 pentagon = new FractalRoot(frameCount);
 pentagon.drawShape();
}
#A Reduced to 4 to keep manageable

You make one other little change here: you pass the frameCount to the FractalRoot object. If you then modify FractalRoot, you can use that number to increment the angle and make the shape spin, Space Odyssey style:

FractalRoot(float startAngle) {
...
   float x = centX + (400 * cos(radians(startAngle + i)));
   float y = centY + (400 * sin(radians(startAngle + i)));
...
}

Also notice that the strut ratio doesn’t just vary between 0 and 1, we’ve allowed it to go to 2, so it can extend beyond the limits of the shape. Does this work? Try it and see. How about if the strut ratio went into negative values? What would happen then? Only one way to find out:

_strutFactor = (noise(_strutNoise) * 3) - 1;

In this case, the ratio varies between -1 and +2. You end up with an animation that produces shapes like those shown in figure 6.

Figure 6 Strut factors -1 through to +2

Already it’s getting interesting.

Although the pentagon has a neat symmetry, there is no reason why your starting shape has to have five sides. One of the reasons I adopted the non-absolute method of drawing the pentagon was that it made it easy to add extra sides to the polygon. For example, create a global variable:

int _numSides = 8;

Now, change the FractalRoot class as shown in this listing.

Listing 5 Sutcliffe Pentagon, not limited to five sides

class FractalRoot {
 PointObj[] pointArr = {};
 Branch rootBranch;

 FractalRoot(float startAngle) {
   float centX = width/2;
   float centY = height/2;
   float angleStep = 360.0f/_numSides;                  #A
   for (float i = 0; i<360; i+=angleStep) {
    float x = centX + (400 * cos(radians(startAngle + i)));
    float y = centY + (400 * sin(radians(startAngle + i)));
    pointArr = (PointObj[])append(pointArr, new PointObj(x, y));
   }
   rootBranch = new Branch(0, 0, pointArr);
 }

 void drawShape() {
  rootBranch.drawMe();
 }
}
#A Steps according to number of sides

Now you have a structure capable of handling any number of sides. How about eight, as shown in figure 7?

Figure 7 Eight sides: a Sutcliffe Octagon?

Or 32? (See figure 8.)

Figure 8 A 32-sided structure

Taking another approach, why should you feel limited to 360 degrees? I discovered that setting the angle step to 133 and stepping 8 times (effectively turning the circle three times) gave me something like figure 9.

Figure 9 Angle step of 133, through 1024 degrees

I followed this line of exploration by making the angles and the number of sides vary according to a noise factor between frames, in the same way you varied the strut factor. You shouldn’t have any difficulty working out the code if you want to try it. Through this process, I discovered what an incomplete revolution looks like (see figure 10).

Figure 10 The system is resilient enough to work with irregular and incomplete shapes.

The fractal structure I built (to Sutcliffe’s specification) proved to be extremely mathematically sturdy, as long as I didn’t turn the number of levels up too high. This meant I could wrench it about as if I were throttling a ferret, with the only consequence being that the results became less linear and more interesting.

Summary

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.

Programming generative art, if I were to try and sum up it up into a single pocket-sized nugget, is about allowing the chaos in. Not all things in life benefit from a structured approach; there is much that is better for a little chaotic drift. If you have a way of working that allows this freedom, permitting inspiration to buffet you from idea to idea, you’ll inevitably end up in some interesting places.

Here are some other Manning titles you might be interested in:

image024.jpg

Secrets of the JavaScript Ninja
John Resig and Bear Bibeault

image025.jpg

Android in Action, Second Edition
W. Frank Ableson, Robi Sen, and Chris King

image026.jpg

iPhone and iPad in Action
Brandon Trebitowski, Christopher Allen, and Shannon Appelcline

 

[1] Alan has written about the early days of the CAS in the book White Heat Cold Logic: British Computer Art 1960–1980, Paul Brown, Charlie Gere, Nicholas Lambert, and Catherine Mason, editors (MIT Press, 2008).

[2] Kenneth Stephenson, Introduction to Circle Packing: The Theory of Discrete Analytic Functions (Cambridge University Press, 2005)

[3] J. W. Cannon, W. J. Floyd, and W. R. Parry, “Finite subdivision rules,” Conformal Geometry and Dynamics, vol. 5 (2001)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Manning
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Slacker00722-Jun-11 0:42
memberSlacker00722-Jun-11 0:42 
QuestionMy vote of 5 Pin
rippo21-Jun-11 20:00
memberrippo21-Jun-11 20:00 
AnswerRe: My vote of 5 Pin
rippo21-Jun-11 20:01
memberrippo21-Jun-11 20:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150707.1 | Last Updated 21 Jun 2011
Article Copyright 2011 by Manning
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid