Artful Computing

Now start up Processing and cut and paste the following program into the edit window, and run it to see an animation. (Or else download the complete program here.)

// 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.

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).

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

int   nsides = 500;           // 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 = -1;                // For counting the number of times draw() has been called.

float x[];
float y[];
float x1[];
float y1[];

int cr[];
int cb[];
int cg[];


void setup()                  // Executed once when Processing starts-up.
{
  size(1000,1000);            // 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.
  
  x = new float[hmax];        // Used to draw the circle radii - x[i] coordinate of current circumference point on i'th circle
  y = new float[hmax];        // Used to draw the circle radii - y[i] coordinate of current circumference point on i'th circle
  
  x1 = new float[nsides+1];   // This remembers the x-coordinates of the points used to define the final figure.
  y1 = new float[nsides+1];   // This remembers the y-coordinates of the points used to define the final figure.
  
  
  cr = new int[hmax];         // Array to be used for Red colour component for each harmonic figure.
  cg = new int[hmax];         // Array to be used for Green colour component for each harmonic figure.
  cb = new int[hmax];         // Array to be used for Blue colour component for each harmonic figure.

  for(int h=0;h<hmax;h++)
    {
      r[h] = 0.0;             // Need to ensure that all circle radii are initialised - set all to zero to start with.
      p[h] = 0.0;             // Phases not  used.
      f[h] = 1.0+(h-1)*1;     // Set frequencies to be n=k*mod(m) to give (m-1)-fold figure symmetry.
      
      cr[h] = 200;            //
      cg[h] = 200;
      cb[h] = 200;
    }

  r[0] = width/5;             // The fundamental size scale;
  r[1] = 1.0;                 // The first harmonic amplitude. (I.e radius of first circle.)
  r[2] = 0.8;                 // The second harmonic amplitude. (I.e. radius of second circle.)
  r[3] = 0.4;                 // The third harmonic amplitude. (I.e. radius of third circle.)
  r[4] = 0.2;                 // The fourth harmonic amplitude. (I.e. radius of fourth circle.)

  cr[1] = 0;   cg[1] = 0;   cb[1] = 200;   // Define colour to use for first harmonic circle
  cr[2] = 200; cg[2] = 0;   cb[2] = 0;     // Define colour to use for second harmonic circle
  cr[3] = 0;   cg[3] = 200; cb[3] = 0;     // Define colour to use for third harmonic circle
  cr[4] = 0;   cg[4] = 200; cb[4] = 200;   // Define colour to use for fourth harmonic circle
  
  background(100,100,100);

  frameRate(50);      // Make this smaller if you want more time to see what is happening.
}

void draw()
{
  count++;
  
  translate(xc,yc);            // Make origin of coordinate system the centre of the drawing area.  

  float theta = count*ith;     // The angle made from the horizontal by the line
                               // from the polygon centre to the current vertex.
  
  if(count <= nsides)
    {
    background(200);            // Make the background colour grey - erasing everything
                                // from the last time draw() was executed.                                              
    x1[count] = 0.0;
    y1[count] = 0.0;
    
    x[0] = 0.0;
    y[0] = 0.0;
    
    for(int h=1;h<hmax-1;h++)            // Leave f[9] for perp line
      {
        stroke(255,255,255,30);          // Draw the outline in white.
        fill(cr[h],cg[h],cb[h],30);      // Fill with a harmonic specific colour specified in setup()
        
        ellipse(x1[count],y1[count],2*r[0]*r[h],2*r[0]*r[h]);       // Draw a circle centred at the position specified by the sum of previous harmonics.

        x1[count] = x1[count] + r[0]*r[h]*cos(f[h]*(theta+p[h]));   // Accumulate the sum of x components of each circle's radius
        y1[count] = y1[count] + r[0]*r[h]*sin(f[h]*(theta+p[h]));   // Accumulate the sum of y components of each circle's radius
        
        x[h] = x1[count];                                           // Remember the partial sum so we can draw each circle radius.
        y[h] = y1[count];
        
        strokeWeight(3);
        stroke(cr[h],cg[h],cb[h],100);
        line(x[h-1],y[h-1],x[h],y[h]);                              // Line from the centre of the current circle to its current circumference point.

      }

    }
  strokeWeight(2);
  stroke(255,255,255,100);
  
  for(int i=1;i<=nsides;i++)                        // Draw (or redraw) all parts of the polygon calculated so far.
  {
    line( x1[i-1],y1[i-1], x1[i],y1[i]);            // draw the line giving one segment of the polygon.
    
  }

}

 

It becomes a bit tedious to experiment with different figures by varying the amplitude and phase of the harmonics by each time stopping the program, modifying the values defined in setup() and restarting. We can interactively modify amplitudes by adding the following sub-programs. These respond to mouse movement and key-presses and interactively modify amplitudes and phases.

void keyPressed()
{  
  if(key == '0' ) { r[0] = float(mouseX)/2.0;}
  if(key == '1' ) { r[1] = float(mouseX)/float(xmax); p[1] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '2' ) { r[2] = float(mouseX)/float(xmax); p[2] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '3' ) { r[3] = float(mouseX)/float(xmax); p[3] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '4' ) { r[4] = float(mouseX)/float(xmax); p[4] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '5' ) { r[5] = float(mouseX)/float(xmax); p[5] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '6' ) { r[6] = float(mouseX)/float(xmax); p[6] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '7' ) { r[7] = float(mouseX)/float(xmax); p[7] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '8' ) { r[8] = float(mouseX)/float(xmax); p[8] = TWO_PI*float(mouseY)/float(ymax);}
  if(key == '9' ) { r[9] = float(mouseX)/float(xmax); p[9] = TWO_PI*float(mouseY)/float(ymax);}
}

The subroutine responds to a key-press, and reads the current position of the mouse. For example, if key "2" is pressed, then the horizontal position of the mouse in the canvas is used to determine a new amplitude of the 2nd harmonic and the vertical position the phase offset. When the "0" key is pressed the horizontal position is used to determine the overall scale of the figure.

We will also find that we want to save images from our programs. In particular, we may want to save an image while a figure is building up over time. To do this we can include the following subroutine.

void keyReleased()
{
  // This is called by the Processing system once whenever a key is released.
  // The value of the key is stored in the "key" global variable.
  // We use the "released" event as a user signal for certain actions
  // because we can guarantee that it is a discrete event.
  // (In contrast the keyPressed() routine is called multiple times while a key is held down.)

  if(key == 's') { saveFrame("Output-####.jpg");}   // Save a copy of the current image as a JPEG file.
                                                    // The name of the time is constructed according to
                                                    // the exact time the event was actioned so multiple
                                                    // requests always generate new files.

  if(key == 'e') { background(200,200,200);} // Erase the previous figure and create a new background.
}
 

Pressing the "s" key saves a JPEG image. It will have a name of the form "Output-####jpg" where #### is replaced by a unique integer so we can save multiple images from our program run. The above program also include an erase operation that occurs when the "e" key is presssed.

The following figures show two versions of epicycle generation with interactive control.

In this sketch, after the curve has finished drawing, you can put the mouse inside the figure click it and then press one of the numerical keys. It will redraw the figure with the amplitude and phase of the harmonic component corresponding the number of the key modified. Moving the mouse left to right changes the amplitude of the harmonic, moving it up and down changes the relative phase. 
 This figure also draws epicycles, but modifies the complete curve interactively. Again, click with the mouse on the figure, then depress one of the numerical keys and keep it depressed while you move the mouse. It will redraw the figure with the amplitude and phase of the harmonic component corresponding the number of the key modified. Moving the mouse left to right changes the amplitude of the harmonic, moving it up and down changes the relative phase.

See my Harmonics page for a more in depth discussion of Fourier Series and an further example of using epicycles to produce a square!

Now let us see how we can use this to produce some more interesting images, using a slightly more elaborate program described on the next part.