Title: Actionscript & Trigonometry
Level: Advanced
Author: John Baines
You can download the zipped FLA File
here.

The Moving Finger writes; and, having writ,
Moves on: nor all your Piety nor Wit
Shall lure it back to cancel half a Line,
Nor all your Tears wash out a Word of it.

Omar Khayyam

Introduction

The brief for this tutorial was to walk-through some actionscript which uses Flash MX's built-in trigonometric functions and the drawing API to draw flower-like shapes.

If you aren't familiar with the Flash drawing methods or you want to see some examples of how to draw various shapes, I recommend you look at the tutorials listed in the Links section.

The flower is generated entirely at run-time using only actionscript without using any graphical library elements.  Although not strictly necessary, I have used Illogicz's slider component because it makes it easy to change the parameters used to draw the flower.  The component is included in the fla file so you don't have to install the component to follow the tutorial.  However, as it is so useful, there is a link in the Links section.


The SWF file

Here is the completed SWF file - a flower-like shape drawn using a series of lineTo() commands.

There are the two sliders on the right of the stage which change the appearance of the flower.

Adjust the "number of petals" slider and you will see that it changes the number of petals.

Adjust the "complexity" slider and you will see that the complexity of the flower changes.

Below the two sliders is a faint button that gets brighter when you roll the mouse over it.  The button toggles the drawing mode.  The first time you press it the flower is re-drawn in what I'll refer to as fill-mode.  You'll see the same flower shape but this time it has been drawn using thicker lines radiating outwards from the centre.

Remember that you can toggle between the fill-mode and outline-mode displays at any time.  Go ahead and press the button.

You'll see that another two sliders become visible but don't play with them until you have read the next section.


The Cheat

When you draw a shape using lineTo(), the result is an outline of the shape made up of sections of straight line.  If you want to fill the shape with solid colour you can use the beginFill() method.  Unfortunately, using this method has unpredictable and unwanted effects when you plot an open curve or one that that crosses itself like the flower.

However, the good news is that you can simulate a filled curve by simply drawing a thick line from the centre to each calculated point on the curve.  By using a partially transparent line and changing the thickness of the line and the angle step between the calculated points, the areas where the lines overlap result in a lot more structure to the flower.  This simple trick, which uses only a few lines of code to implement, is the what you see in fill-mode.

      Beware - thick lines drawn with small steps take a lot of processing power!

Remembering the above warning, go back to the SWF file above and experiment with the sliders in fill-mode.  The "petal width" slider changes the thickness of the line drawn from the centre to each point between 4 pixels to 44 pixels.  The "detail" slider changes the angular interval at which r is calculated between one point every 8 degrees (low detail) and one point every 3 degrees (fine detail).


The Maths

The lobed, flower-like pattern originates in the periodicity of the cosine function.  The maths is quite simple so don't be intimidated.

The flower shape is calculated using the relationship

r = 1 + d.cos(c.ø)

where r is the radius of the flower at the angle ø, and both d and c are the parameters that affect the shape of the flower.  Because the resulting curve looks quite plant-like, this function is known, not surprisingly, as the Botanic Curve; (see Jan Wassenaar's excellent site listed in the Links section).  This simple polar equation can result in some surprisingly complex shapes simply by changing the parameters c and d.  

Parameter c determines how many times the radius passes through a maximum in each revolution of 360 degrees.  This in turn determines how many "petals" the curve has so c = 3 gives three petals (a trefoil), c = 4 gives four petals (a quadrifolium), and so on.   Because we want the curve to be periodic with a fixed number of petals per revolution, we restrict the possible values of c ntegers greater than 3.

Parameter d has the effect of modulating the variation of the main curve.  Because the minimum value of the cosine term is -1 you can see that if d = 1, r will have a minimum value of 1 + (-1) = 0 and the curve will return to the origin.  If d = 0, r is constant and so the flower will be circular.  If d is less than 1, the curve will turn back towards the origin but won't reach it, for instance, if d = 1/2, the curve will return half-way to the origin.  Finally, if d is greater than 1, the minimum value for the d.cos(c.ø) term will be less than -1 so the curve will go beyond the origin and a new petal will be formed diametrically opposite the main petal.  Once again, because we want the curve to be periodic, we restrict the possible values of d to either integers or rational numbers like 1/n where n is an integer.

So to draw a flower using this formula, we just need to calculate how the radius, r varies with the angle, ø then scale and convert these values to x and y coordinates that we can plot using Flash's moveTo() and lineTo() methods.  It is as simple as that.

In the completed SWF, the slider labelled "number of petals" changes parameter c between 3 and 16 and so we see a flower with between 3 and 16 petals.  The slider labelled "complexity" changes an intermediate variable sepals between -6 and + 6.  Variable sepals is used to scale parameter d between 1/6, 1/5, 1/4 ... 4, 5, 6 and so we see a flower with incisions of varying depth (where d is less than 1) and with extra petals (where d is greater than 1).  When d = 0, the flower is circular.


The FLA file - You can download the zipped FLA file here

Download the FLA file and open it in Flash MX.  This section is meant to be read in conjunction with the comments in the FLA file.

The stage is 600 x 400 pixels and the stage colour is black.

There are four instances of the Illogicz slider component (Skin 2) on the stage.  Each slider instance has the Change Handler method set to reset and the Target Instance property set to _root.  The table below lists the other component parameters that are set in the Properties Panel.

Instance Name
Minimum value
Maximum value
Initial value
Target property
petalsSlider
3
16
12
petals
sepalsSlider
-6
-2
6
sepals
thicknessSlider
4
44
30
lineThickness
dThetaSlider
3
8
6
dTheta

Below the sliders there is a movieclip with the instance name fillStroke_btn which has actions assigned in the functions layer to the onRelease(), onRollOver() and onRollOut() events.

Note that the text labels have been broken apart to avoid problems if you don't have the Star Bold Condensed font installed.  The text labels for thicknessSlider and dThetaSlider are inside a movieclip whose instance name is toHide.

The last object on the stage has an instance name background and is a filled rectangle with _alpha set to 60.  It is there for aesthetic reasons and has no other function.

Here is the actionscript from the Actions Layer in the fla file.

// create the clip for drawing and place it on the stage
flower = createEmptyMovieClip("rhodonea", 1);
flower._x = 220;
flower._y = 200;
// set the maximum radius of the flower to 160 pixels
scale = 160;
// set the default drawing mode to contour
fill = false;
// use a red line
lineColour = 0xFF0000;
// call function to handle initialisation
reset();

Here is the actionscript from the Functions Layer in the fla file.

The reset function does the following.
  handles the visual appearance of the stage
  sets the properties for the line used to draw the flower
  calls the function to calculate the shape of the flower
  clears the drawing clip and moves to the first calculated point
  sets the lineStyle for the drawing clip
  initialises a timer to plot the calculated points at 1 millisecond intervals

reset = function() {
// If fill is true, we are plotting the fill.
// If fill is false, we are plotting the outline.
// Hide or show the unused sliders and labels.
   thicknessSlider._visible = fill;
   dThetaSlider._visible = fill;
   toHide._visible = fill;
   if (fill) {
// We are plotting the filled flower so use a lower line opacity
// and get the variables from the sliders.
// petals = the number of petals in the flower.
// sepals = the modulating term which generates sepals.
// dTheta = the angle between each plot point (degrees).
// lineThickness = the line thickness (pixels).
       lineAlpha = 25;
       petals = petalsSlider.getValue();
       sepals = sepalsSlider.getValue();
       dTheta = dThetaSlider.getValue();
       lineThickness = thicknessSlider.getValue();
// Set the button label.
       fillStroke_btn.gotoAndStop(1);
   } else {
// We are plotting the flower outline so use a higher line opacity
// and appripriate thickness and step size.
       lineAlpha = 100;
       lineThickness = 4;
       dTheta = 1.5;
// Set the button label.
       fillStroke_btn.gotoAndStop(2);
   }
// Calculate the shape of the flower.
   calculateFlower();
// Clear the flower clip and (re)set the properties for the line.
   flower.clear();
   flower.moveTo(x[0], y[0]);
   flower.lineStyle(lineThickness, lineColour, lineAlpha);
// (Re)set the timer which calls the function to plot the flower.
   clearInterval(plottingFlower);
   plottingFlower = setInterval(plotFlower, 1);
// Inialise the loop counter for plotFlower() function
   loop = 0;
};

The calculateFlower function is called by reset().   It uses the petals and sepals variables which are set by the petalsSlider and sepalsSlider sliders to calculate the shape of the flower using the Botanic Curve formula

r = 1 + d.cos(c.ø)

Parameter c is obtained directly from the variable petals.  Because the sepalsSlider returns integer values between -6 and +6, parameter d is calculated as 1/|sepals| if sepals is negative or equal to sepals otherwise.
The angle increment dTheta is obtained from the dThetaSlider slider.
Because I find it easier to think in degrees than radians, angles are expressed in degrees and converted to radians using the degToRad() function.
The calculated radius is scaled to the required size by a multipiclative factor scale/(1+d).
Finally, the polar coordinates (r,ø) are converted to the rectangular coordinates (x,y) that are used by the function plotFlower().

calculateFlower = function () {
// create arrays for storing the coordinates of the flower.
    x = new Array();
    y = new Array();
    count = 0;
    if (sepals < 0) {
       d = 1 / Math.abs(sepals);
   } else {
       d = sepals;
   }
   for (theta=0; theta<=360; theta += dTheta) {
       t = degToRad(theta);
       r = scale/(1+d)*(1 + d*Math.cos(petals*t));
       x[count] = r*Math.cos(t);
       y[count] = r*Math.sin(t);
       count++;
   }
};

The plotFlower function draws the flower point by point.  It is called every millisecond by the timer ID plottingFlower.

plotFlower = function () {
// If fill is true, move to the centre point.
   if (fill) {
       flower.moveTo(0, 0);
   }
// Increment the loop counter.
   loop++;
// Compare the loop counter with the variable count (calculated by calculateFlower()).
// If all the points have been plotted, clear the timer.
   if (loop==count-1) {
       clearInterval(plottingFlower);
   }
//Draw a line to the calculated position.
   flower.lineTo(x[loop], y[loop]);
};

The degToRad function, called by calculateFlower(), takes an angle in degrees and returns the angle in radians.

degToRad = function (deg) {
   return (deg/180)*Math.PI;
};

Finally, three functions which define the behaviour of the button whose instance name is fillStroke_btn. When the button is clicked, the value of the boolean variable fill is toggled (i.e. it alternates between true and false), and the function reset() is called.

fillStroke_btn.onRelease = function() {
   fill = !fill;
   reset();
};

The transparency of the button changes when the mouse rolls over and rolls out.

fillStroke_btn.onRollOver = function() {
   this._alpha = 100;
};

fillStroke_btn.onRollOut = function() {
   this._alpha = 20;
};

That's the code. I hope it makes sense.

Why not create your own plots by changing the calculateFlower() function.  Follow the maths links and you'll find more interesting curves than you can shake a stick at!

If you need any help with this tutorial or if you find any bugs or typos, please drop me a line at the forum and I'll try to help.

     cheers,

     bainsey


Links

1. Maths links

Jan Wassenaar's Botanic Curve page at 2dcurves.com.
A page on the related Rhodonea curve from the Plane Curves section at Mathworld.
The Famous Curves Index at the University of St Andrews
The Visual Dictionary of Special Plane Curves by Xah Lee.
Simon Singh's 5 Numbers page at the BBC.
Links to just about anything mathematical at the geometry junkyard
A treatise on the Aesthetics of Symmetry by Jeff Chapman

2. Flash MX drawing methods links

Nuts & Bolts: Drawing Tools tutorial by Helen Triolo at Actionscript-Toolbox.com.
Drawing API tutorial by Ilyas Masa (aka Ilyas Usal, aka Pom) at kirupa.com.
Advanced Drawing Methods by Ric Ewing at Macromedia Developer Center.
Code and Only Code: drawing methods by various authors at Flashcoders Wiki.

3. Components links

Illogicz component set including the slider bar used here at Flashcomponents.net.