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