Artful Computing

 

We now have all the parts in hand. The program below has been used to generate most of the figures in the "Epicycles" Gallery. However, just pressing the "Run" button produces a rather boring image - a fuzzy circle. All we are seeing here is the first harmonic - a circle, where the points that are iterated over on the curve are each given an additional small motif - a short line perpendicular to the curve and one at a tangent to the curve.

Things only become interesting when we start to use the interactive features. That is where you start to add value to the generated image. Outputs from the example program (above)  illustrates what is possible. The example program below show how epicycle curves are constructed - this just shows the setup() and draw() parts of the program. (The full program including additional subroutines, keypressed() and keyReleased(), for interactivity and saving images can also be downloaded here.) Just paste it into the Processing code window and run.

// Variables defined here are "Global" that is, accessible to every sub-program for reading and writing.
// We need to declare here some of the variable whose values will be defined in the setup() routine
// and later used in the draw() routine and its sub-programs.

float ampr=50;        // Amplitude of variation of RED component of colour.
float ampg=200;        // Amplitude of variation of GREEN component of colour.
float ampb=55;        // Amplitude of variation of BLUE component of colour.

int   r_zero = 10;    // Level around which R component varies with amplituded ampr;
int   g_zero = 100;    // Level around which G component varies with amplituded ampg;
int   b_zero = 200;    // Level around which B component varies with amplituded ampb;

float p_red   = 0.7;    // Number of radians that takes R component through its full range of variation.
float p_green = 0.8;   // Number of radians that takes G component through its full range of variation.
float p_blue  = 0.9;   // Number of radians that takes B component through its full range of variation.


float dpr;
float dpg;
float dpb;

int xmax;                     // xmax and ymax will hold the x and y extent of the canvas,
int ymax;                     // which it is useful to hold in variables so we can refer to them by names.
int xc;                       // x0 and y0 will define the centre of the canvas
int yc;                       // which is often useful to refer to by name.

int hmax = 10;                // Number of harmonics allows (including zeroth).

int hmode = 0;

float[] r;                    // The radius of our epicycle harmonics5 in pixels.
float[] p;                    // Phases of each harmonic - these will be initialised in setup();
float[] f;                    // The relative frequency of each epicycle;

int   nsides = 10000;           // The number of side in our regular polygon approximating the circle.
float ith    = TWO_PI/nsides; // Increment in angle for each side of the figure.
                              // Note the use of radians (TWO_PI) for the total angle around a circle.

float tdeg = 180.0/PI;        // Conversion factor from radians to degrees.

int count = 0;                // For counting the number of times draw() has been called.

void setup()                  // Executed once when Processing starts-up.
{
  size(1200,1200);            // Size of our canvas - has to be explicit integers by Processing rules.
  
  xmax = width;               // It is useful to refer to the canvas size symbolically
  ymax = height;              // so we can remember what we mean when using "400" etc..
  xc   = xmax/2;              // We can now work out the coordinates of the canvas centre.
  yc   = ymax/2;              // 
  
  r = new float[hmax];        // Array of harmonic amplitudes
  p = new float[hmax];        // Array of harmonic phases
  f = new float[hmax];        // The harmonic frequencies.
  
  dpr = TWO_PI/p_red;         // Increment of RED phase
  dpb = TWO_PI/p_blue;        // Increment in BLUE phase
  dpg = TWO_PI/p_green;       // Increment in GREEN phase  

  for(int h=0;h<hmax;h++)
    {
      r[h] = 0.0;
      p[h] = 0.0;
      f[h] = 1.0+(h-1)*5;     // Set frequencies to be n=k*mod(m) to give (m-1)-fold figure symmetry.
    }

  r[0] = width/5;           // The fundamental size scale;
  r[1] = 1.0;                 // The first harmonic amplitude. The rest initialised to zero in setup();
  r[9] = 0.1;
  
  background(200,220,220);
  stroke(255,255,255,50);        // Draw the outline in white.
}

void draw()
{
  count++;
  
  translate(xc,yc);            // Make origin of coordiante system the centre of the drawing area.
  
  //background(100);            // Make the background colour grey - erasing everything
                              // from the last time draw() was executed.

  //r[0] = r[0]*0.995;
  
  for(int i=0;i<nsides;i++)   // Iterate round the vertices of the polygon approximating a circle
    {
      float theta = i*ith;    // The angle made from the horizontal by the line
                              // from the polygon centre to the current vertex.

      // Define current rgb colour
      int red   = r_zero +int( ampr*sin( dpr*theta ) );
      int blue  = b_zero +int( ampb*cos( dpb*theta ) );
      int green = g_zero +int( ampg*sin( dpg*theta ) );
                                            
                                            
      float x1 = 0.0;          //
      float y1 = 0.0;
      float x2 = 0.0;
      float y2 = 0.0;
      float x3 = 0.0;
      float y3 = 0.0;
      
      //if(pflag == -1 && i == 0) {pflag = 1;}
      
      for(int h=1;h<hmax-1;h++)            // Leave f[9] for perp line
        {

          x1 = x1 + r[h]*cos(f[h]*(theta+p[h]));
          y1 = y1 + r[h]*sin(f[h]*(theta+p[h]));

          x2 = x2 + r[h]*cos(f[h]*(theta+p[h]+ith));
          y2 = y2 + r[h]*sin(f[h]*(theta+p[h]+ith));

          x3 = x3 + r[h]*cos(f[h]*(theta+p[h]+ith*2));
          y3 = y3 + r[h]*sin(f[h]*(theta+p[h]+ith*2));  

        }
        
      x1 = x1*r[0]; y1 = y1*r[0]; 
      x2 = x2*r[0]; y2 = y2*r[0];
      x3 = x3*r[0]; y3 = y3*r[0];
      
      stroke(255,255,255,5);
      line( x1,y1, x2,y2);            // draw the line giving one segement of the polygon.

// The geometry here is not trivial, it is about finding the angle of bend
// at each vertex of the polygon and then bisecting it to draw a line perpendicular to the curve,
// and also establish tangent lines at right angles to the perpendicular.
// Expect to have use your large-size thinking cap if you want to work out what is going on here. float theta1 = atan2((y2-y1),(x2-x1)); if(theta1 < 0.0) { theta1 = TWO_PI+theta1;}; float theta2 = atan2((y3-y2),(x3-x2)); if(theta2 < 0.0) { theta2 = TWO_PI+theta2;}; float dtheta=theta2-theta1; if(theta2 > theta1) { dtheta = PI+theta2-theta1; } else { dtheta = TWO_PI+PI + theta2-theta1; } if(dtheta < 0.0) {dtheta = TWO_PI - dtheta;} float alpha = PI-dtheta/2.0; float x4 = x2 + r[0]*r[9]*cos( theta1 -alpha ) ; float y4 = y2 + r[0]*r[9]*sin( theta1 -alpha ) ; float x5 = x2 + r[0]*r[9]*cos( theta1 -alpha - HALF_PI) ; float y5 = y2 + r[0]*r[9]*sin( theta1 -alpha - HALF_PI ) ; float x6 = x2 + r[0]*r[9]*cos( theta1 -alpha + HALF_PI ) ; float y6 = y2 + r[0]*r[9]*sin( theta1 -alpha + HALF_PI ) ; stroke(red,green,blue,5); line( x2,y2, x4,y4); // Make a line perpendicular to the curve. line( x2,y2, x5,y5); // Make for rearward moving tangent line. line( x2,y2, x6,y6); // Make a forward moving tangent line. } }