Circles, Spirals and Sunflowers, Part 1

28 Oct

In this tutorial, my aim is to show you some fun ways to draw circles, and spirals, and ultimately, how to draw the interesting pattern you see on sunflowers. All these techniques are described from a few basic facts about circles, which you probably learned (and forgot) a long time ago. Back in grade school, you may have learned these three equations:


D (diameter) = 2 R
C (circumfrence) = 2 π R (or π D)
A (area) = π R2

While these equations are useful, they aren’t actually needed to draw a circle. We’ll make use of them in a surprising way, below.

To draw a circle or anything, we are going to need a graphics framework. I’m going to use Processing.js. If you are using an older web browser, like IE 8 or earlier, you’re not going to see anything!

Example 1 shows a basic black circle. I’ve made the code longer than it needs to be, to make it readable. Here’s what it looks like.

  size(100, 100);
  smooth();

  background(255);

  float cx = width/2;
  float cy = height/2;
  float diameter = width*.9;
  
  fill(0);
  ellipse(cx,cy,diameter,diameter);

The most complicated piece of this is the ellipse function, which is used to draw circles and ovals. It’s parameters are

  • cx, cy – the center coordinates of the circle you are drawing.
  • width, height – the dimensions of the circle you are drawing. For circles (as opposed to ovals) these two numbers are the same, and correspond to the diameter of the circle.

Example 2 shows a more complicated taijitu figure drawn using multiple filled circles (and one half circle). This is a more advanced example and can be skipped for now. If you’re curious about it, click on the link for example 2 and scroll to the bottom of the code to read more about it. Later, try reproducing it without looking at the code.


Note that if you use stroke() instead of fill(), you get an outlined circle, which looks like example 3.

At this point, I would suggest, as a hands-on exercise, that you construct a sample web page that draws a circle using Processing.js. You can use my ‘example 3’ code as a starting point – click on one of the examples and do a “view source” to see what’s going on. After that, take a break and come back when you’re feeling fresh!

Feeling refreshed? Great. You may be curious how the points on these circles are plotted.

In example 4, I’ve drawn a large circle out of small circles, like a black pearl necklace. There are a number of ways to figure out how the points lie on a circle, but I usually use the sine and cosine functions to do it. I think of these functions as “circle drawing” functions. The basic equations are:

x = cx + cos(θ) R
y = cy + sin(θ) R

These are the classic equations for converting from polar coordinates (angle and distance from some center point) to cartesian coordiantes (x and y). In these equations, cx and cy are the center point of the circle, R is the radius of the circle, and theta (θ) represents the angle going around the circle. In code, you usually supply the angle in radians, not degrees. The more familiar degrees go from 0 to 360. Radians go from 0 to 2π. To convert a number from degrees to radians, multiply it by a scaling constant (π / 180), or just use the radians() function which is built into Processing. Note that the angle value you pass to sin and cosine doesn’t need to be restricted to 0 to 2π – you can keep going around the circle in either direction. sin(θ) will produce identical values for any two numbers which are 2π apart. The pattern produced by these functions are sine waves and cosine waves (cosine waves are basically sine waves which are out of phase by 90 degrees).

In Processing.js, I employ these equations in a loop, to draw each point on the circle, like so:

for (int i = 1; i <= nbr_circles; ++i) {
  float angle = i * TWO_PI / nbr_circles;
  float x = cx + cos(angle) * lg_rad;
  float y = cy + sin(angle) * lg_rad;
  ellipse(x, y, sm_diam, sm_diam);
}

In this code, lg_rad is the radius of the large ring, and sm_diam is the diameter of the smaller circles I am drawing in the ring. In order to make the circles just big enough to touch, I worked out the circumfrence of the large circle, and then divided it by the number of circles in the ring. This is the diameter of the smaller circles.

// large circle's diameter
float lg_diam =  width * .85;   

// large circle's radius
float lg_rad  = lg_diam/2;      

// large circumfrence
float lg_circ =  PI * lg_diam;  

// small circle's diameter
float sm_diam = (lg_circ / nbr_circles); 

By changing the value of nbr_circles, I can increase or decrease the number of circles in the ring, as in example 5. Try hitting the play button on this example - you'll see the circles get smaller as I increase the number with each frame. If I make the circles small enough, it closely resembles the circular outline in example 3. At those sizes, it would be far more efficient to draw individual pixels, rather than little tiny circles!

While you take a break, consider how this code could be modified to draw a spiral instead of a circle...

Concluded in Part 2.

Tags: , , , ,

4 Responses to “Circles, Spirals and Sunflowers, Part 1”

  1. Danielle December 10, 2012 at 1:29 pm #

    Hi! I am wondering whether there is a way to separate the small circles, in the example above where the small circles make up a large circle, so that each small circle can be edited independently from the others?

    • jbum December 10, 2012 at 3:45 pm #

      Yes, that is indeed possible. I’ll post an example later… Not entire surely what you mean by “edit” though… Certainly each sub-circle can be individually controlled.

      • Danielle December 11, 2012 at 1:39 am #

        Thanks! Yes I meant indivdually controlled, so each sub-circle can change colour, size etc

        • jbum December 11, 2012 at 10:38 am #

          The circles are drawn in this loop, in which the i variable counts up.

          fill(0);
          for (int i = 1; i <= nbr_circles; ++i) { float angle = i * TWO_PI / nbr_circles; float x = cx + cos(angle) * lg_rad; float y = cy + sin(angle) * lg_rad; ellipse(x, y, sm_diam, sm_diam); }

          You can use random numbers to control the color, and size and offset
          of each circle.

          fill(0);
          noStroke();

          for (int i = 1; i <= nbr_circles; ++i) { fill(random(255)); float angle = i * TWO_PI / nbr_circles; float x = cx + cos(angle) * lg_rad; float y = cy + sin(angle) * lg_rad; float d = random(3,sm_diam); float ox = random(-3,3); float oy= random(-3,3); ellipse(x+ox, y+oy, d, d); }

          Here's a similar loop where I draw the points of a star, but the points are individually moving, controlled by the noise generator, rather than random numbers. This way, the points can move smoothly.

          Using the same technique, in a draw() function -- using noise instead of random -- you can make the individual circles smoothly morph.

Leave a Reply

Your email address will not be published. Required fields are marked *