Drawing any size polygon

A more robust example will allow us to alternate between the two winding methods as well as drag corners around to create overlapping regions of our own choosing. The applet here manages both things without terribly complicated code.

To read more about radio buttons, see "Using the RadioButton Component for a Multiple Choice Question" in the Basic section of flashandmath.com or Chapter 8 of our applets book.

To read more about "drag and drop," see "Drag-and-Drop in Flash" in the Basic section of flashandmath.com or Chapter 4 of our applets book.

The example

The code

Open a new Flash file and open the Components panel and drag to the stage a NumericStepper (assign instance name nsPoints) and two RadioButton components (named rbEvenOdd and rbNonZero). With the Component Inspector panel, make sure the radio buttons have the same group name (which they will by default if you do not modify either) and values 0 and 1, respectively. The NumericStepper object should have a minimum value of 3 and a maximum value of 20 or so. Now enter the following code in the Actions panel:

The first line will enable us to use a RadioButtonGroup object in our code. As is the case with most components, this class has to be explicitly imported. Once we can use it, we define the variable rbgWind to refer to the radio button group

import fl.controls.RadioButtonGroup;

var rbgWind:RadioButtonGroup = rbEvenOdd.group;

The Vector class is new to Flash CS4. It is essentially a typed array. That is, it is an Array (i.e., it has the same properties and methods) for which we declare the type of the entries. This optimizes memory allocation and also allows the compiler to catch type mismatches. Note the odd way that one initializes a Vector object in the constructor. See the full help file for more information about the Vector class.

var vecPoints:Vector.<Sprite> = new Vector.<Sprite>();

var vecWind:Vector.<String> = Vector.<String>(["evenOdd","nonZero"]);

The Sprite object spBoard will contain our points.

var spBoard:Sprite = new Sprite();

spBoard.graphics.lineStyle(0,2);

spBoard.graphics.beginFill(0xEEEEEE);

spBoard.graphics.drawRect(-160,-160,320,320);

spBoard.graphics.endFill();

spBoard.x = 180;

spBoard.y = 200;

addChild(spBoard);

The Shape object shLine will contain the filled polygon. It is separate from spBoard so it can be cleared independently. It is added to spBoard at this time so that it is the lowest (i.e., furthest back) child in the display list for spBoard.

var shLines:Shape = new Shape();

spBoard.addChild(shLines);

The variable spMoving will be assigned to whatever Sprite is clicked for drag-and-drop motion. It has global scope so it can be referenced by more than one function below.

var spMoving:Sprite;

Whenever the nsPoints NumericStepper is changed, the function setupPoints is called. This function removes the listeners from the existing points, and then removes each from the display list of the spBoard object. Finally, it calls the setup function to begin a new exploration with the current value of nsPoints.

nsPoints.addEventListener(Event.CHANGE, setupPoints);

 

function setupPoints(evt:Event):void {

var i:int;

var n:int = nsPoints.value;

for (i=0; i<vecPoints.length; i++) {

vecPoints[i].removeEventListener(MouseEvent.MOUSE_DOWN, startPointDrag);

spBoard.removeChild(vecPoints[i]);

}

setup(n);

}

The following function creates np Sprite objects for the vecPoints array. Each is a small circle (using the preferred drawEllipse method) and is placed appropriately around a large circle on spBoard. Each point is also listening for the MOUSE_DOWN event that will initiate dragging that point when it occurs.

function setup(np:int):void {

var i:int;

vecPoints = new Vector.<Sprite>(np);

for (i=0; i<np; i++) {

vecPoints[i] = new Sprite();

vecPoints[i].graphics.lineStyle(1,0);

vecPoints[i].graphics.beginFill(0xCC0000);

vecPoints[i].graphics.drawEllipse(-8,-8,16,16);

vecPoints[i].graphics.endFill();

spBoard.addChild(vecPoints[i]);

vecPoints[i].addEventListener(MouseEvent.MOUSE_DOWN, startPointDrag);

vecPoints[i].x = 130*Math.cos(2*Math.PI*i/np);

vecPoints[i].y = 130*Math.sin(2*Math.PI*i/np);

}

drawLines();

}

The functions startPointDrag, movingPoint and stopPointDrag manage "drag-and-drop" in a pretty standard way. Notice that we use helper function goodX and goodY to keep the dragging point constrained to the spBoard object.

function startPointDrag(mevt:MouseEvent):void {

spMoving = Sprite(mevt.currentTarget);

stage.addEventListener(MouseEvent.MOUSE_MOVE,movingPoint);

stage.addEventListener(MouseEvent.MOUSE_UP, stopPointDrag);

}

 

function movingPoint(mevt:MouseEvent):void {

spMoving.x = goodX(spBoard.mouseX);

spMoving.y = goodY(spBoard.mouseY);

drawLines();

mevt.updateAfterEvent();

}

 

function stopPointDrag(mevt:MouseEvent):void {

stage.removeEventListener(MouseEvent.MOUSE_MOVE,movingPoint);

stage.removeEventListener(MouseEvent.MOUSE_UP, stopPointDrag);

}

 

function goodX(nx:Number):Number {

if (nx < -160) {

return -160;

}

if (nx > 160) {

return 160;

}

return nx;

}

 

function goodY(ny:Number):Number {

if (ny < -160) {

return -160;

}

if (ny > 160) {

return 160;

}

return ny;

}

The heart of the matter (i.e., the actual use of the drawPath method) is in the drawLines function.

function drawLines():void {

var i:int;

var n:int = vecPoints.length;

var vecCmds:Vector.<int> = new Vector.<int>();

var vecCoords:Vector.<Number> = new Vector.<Number>();

for(i=0; i<n; i++) {

vecCmds[i] = 2;

vecCoords[2*i] = vecPoints[i].x;

vecCoords[2*i+1] = vecPoints[i].y;

}

vecCmds[n] = 2;

vecCoords[2*n] = vecPoints[0].x;

vecCoords[2*n+1] = vecPoints[0].y;

vecCmds[0] = 1;

shLines.graphics.clear();

shLines.graphics.lineStyle(1,0);

shLines.graphics.beginFill(0xFF0000);

shLines.graphics.drawPath(vecCmds, vecCoords,vecWind[rbgWind.selectedData]);

shLines.graphics.endFill();

}

When the rbgWind RadioButtonGroup is changed, we would like the picture to be re-drawn. Happily we do not have to mess with the points -- we simply have to redraw the lines.

rbgWind.addEventListener(Event.CHANGE, windChange);

function windChange(evt:Event):void {

drawLines();

}

We want the application to begin with an example on the screen. We use a triangle for this purpose.

setup(3);

Download

Download the source code (fla) for this applet or all three fla files (compressed) for this tutorial at the following links.

The next page gives a variation of this applet that uses the drawPath command for the curveTo method by adding control points to create a curvy polygon.

Back to Basic Tutorials              Back to Flash and Math Home

We welcome your comments, suggestions, and contributions. Click the Contact Us link below and email one of us.

Adobe®, Flash®, ActionScript®, Flex® are registered trademarks of Adobe Systems Incorporated.