// ShapeBuilder - simple ActionScript drawing API functions
// Version 0.15
//
// Copyright (C) 2006, Michiel Sikma <michiel@thingmajig.org>
// makeMap function map drawer.
// Usage:
// makeMap(line, object, direction,
// color, thickness, radius, speed, master, depth)
// Parameters:
// line name of the resulting movie clip
// object coordinates of the line
// direction the initial direction in which the line moves
// color color of the line (in hexadecimal)
// thickness thickness of the line (in pixels)
// radius radius of the curvature
// speed speed at which the line is drawn (in pixels)
// master container movie clip for the line
// depth depth of the resulting movie clip in the master
// fix whether improper line movements should be amended (true)
// Description:
// Uses makeRoute() to create a multitude of lines on the screen.
// Unlike makeRoute(), it is possible to create comprehensive drawings
// that are not limited to being just from A to B. The object parameter
// must be an array with objects that contain x and y indices. The first
// object inside of the array is seen as the starting position, and from
// there on, one long connected line will be drawn from all the given
// points. The rest of the parameters are passed on to makeRoute().
// Unless the fix argument is false, improper line movements will be
// amended.
function makeMap( mc:String,
object:Object,
direction:String,
color:Number,
thickness:Number,
radius:Number,
speed:Number,
master:MovieClip,
depth:Number,
fix:Boolean):MovieClip
{
if (arguments.length < 9) return;
if (!master[mc])
var container:MovieClip = master.createEmptyMovieClip(mc, depth);
var n:Number = 0;
var dx:Number = 0;
var dy:Number = 0;
var ex:Number = 0;
var ey:Number = 0;
container.finished = false;
function makeSegment(cnt:Boolean)
{
if (cnt) if (direction == "x") direction = "y"; else
if (direction == "y") direction = "x";
if (fix != false) {
var subst:Boolean = false;
if (object[n-1].x < object[n].x) dx = 1; else dx = 0;
if (object[n-1].y < object[n].y) dy = 1; else dy = 0;
// Check whether the line is about to move back into itself.
// If so, fix it by making an extra line.
if (dx != (object[n].x < object[n+1].x) && direction == "x" && n) {
// Add an extra line towards the opposite x direction and
// half the next y coordinate.
ex = object[n].x + (radius * ((dx * 2) - 1));
ey = (object[n].y + object[n+1].y) / 2;
subst = true;
}
if (dy != (object[n].y < object[n+1].y) && direction == "y" && n) {
// Add an extra line towards half the next x coordinate and
// the opposite y direction.
ex = (object[n].x + object[n+1].x) / 2;
ey = object[n].y + (radius * ((dy * 2) - 1));
subst = true;
}
if (subst) {
// This is the actual inserting of an extra coordinate.
var tmp:Array = object.splice(n+1);
object.push({x: ex, y: ey});
object = object.concat(tmp);
}
}
this["mapSegment_"+n] = makeRoute(mc+"_"+n, object[n].x, object[n].y,
object[n+1].x, object[n+1].y, direction, color, thickness, radius,
speed, container, depth + n);
mapIterator=this["mapSegment_"+n].createEmptyMovieClip("mapIterator",1);
mapIterator.onEnterFrame = function()
{
if (this._parent.finished) {
removeMovieClip(this);
if (n > object.length - 2) {
trace("At: "+n+" done.");
container.finished = true;
return;
} else makeSegment(true);
}
};
n++;
};
makeSegment();
return container;
};
// makeRoute function route segment drawer.
// Usage:
// makeRoute(mc, start_x, start_y, end_x, end_y, direction,
// color, thickness, radius, speed, master, depth, zeropoint)
// Parameters:
// line name of the resulting (new) movie clip
// start_x starting position x coordinate
// start_y starting position y coordinate
// end_x ending position x coordinate
// end_y ending position y coordinate
// direction the initial direction in which the line moves
// color color of the line (in hexadecimal)
// thickness thickness of the line (in pixels)
// radius radius of the curvature
// speed speed at which the line is drawn (in pixels)
// master container movie clip for the line
// depth depth of the resulting movie clip in the master
// zeropoint does not set the starting coordinates of the mc if true
// Description:
// Draws a line from x, y to x, y with a straight horizontal and
// vertical line, which is connected with a rounded corner. The resulting
// movie clip will set a boolean named "finished" to true once it's
// fully traced its path. Note that the line variable must be a string.
// It will be created automatically if it does not exist; do not
// supply a direct reference to an existing movie clip.
// Originally developed for the Ector Hoogstad website.
function makeRoute( mc:String,
start_x:Number,
start_y:Number,
end_x:Number,
end_y:Number,
direction:String,
color:Number,
thickness:Number,
radius:Number,
speed:Number,
master:MovieClip,
depth:Number):MovieClip
{
if (arguments.length < 12) return;
if (!master[mc])
var line:MovieClip = master.createEmptyMovieClip(mc, depth);
else
var line:MovieClip = master[mc];
if (!direction || (direction != "x" && direction != "y"))
direction = "x";
// Initialize the basic settings of the line movie clip.
line._x = start_x;
line._y = start_y;
end_x -= start_x;
end_y -= start_y;
line.lineStyle(thickness, color, 100);
line.finished = false;
// Set the start angles for the curve based on the destination.
if (end_x > 0) var direction_x:Number = 1; else
var direction_x:Number = -1;
if (end_y > 0) var direction_y:Number = 1; else
var direction_y:Number = -1;
if (direction_x > 0 && direction_y > 0)
if (direction == "x") var curveStart:Number = 90; else
if (direction == "y") var curveStart:Number = 180;
if (direction_x < 0 && direction_y > 0)
if (direction == "x") var curveStart:Number = 90; else
if (direction == "y") var curveStart:Number = 0;
if (direction_x > 0 && direction_y < 0)
if (direction == "x") var curveStart:Number = 270; else
if (direction == "y") var curveStart:Number = 180;
if (direction_x < 0 && direction_y < 0)
if (direction == "x") var curveStart:Number = 270; else
if (direction == "y") var curveStart:Number = 0;
var curve_x:Number = end_x - (radius * direction_x);
var curve_y:Number = end_y - (radius * direction_y);
var lineCaret_x:Number = 0;
var lineCaret_y:Number = 0;
// Every frame, a segment of the line is drawn.
line.onEnterFrame = function():Void
{
if (!line.step) line.step = 1;
if (line.step == 1) {
// First, draw a line until the position of the curve.
if (direction == "x") {
lineCaret_x += speed * direction_x;
if (((direction_x < 0) && lineCaret_x <= curve_x) ||
((direction_x > 0) && lineCaret_x >= curve_x)) {
lineCaret_x = curve_x;
line.step = 2;
}
} else if (direction == "y") {
lineCaret_y += speed * direction_y;
if (((direction_y < 0) && lineCaret_y <= curve_y) ||
((direction_y > 0) && lineCaret_y >= curve_y)) {
lineCaret_y = curve_y;
line.step = 2;
}
}
line.lineTo(lineCaret_x, lineCaret_y);
}
if (line.step == 2) {
// Then, draw an arc to simulate the curve of the line.
if (!line.curve) line.curve = 0;
if (direction == "x") {
line.moveTo(lineCaret_x, 0);
if (direction_x > 0 && direction_y > 0)
curveDirection = -1;
else if (direction_x < 0 && direction_y < 0)
curveDirection = -1;
else
curveDirection = 1;
} else if (direction == "y") {
line.moveTo(0, lineCaret_y);
if (direction_x > 0 && direction_y > 0)
curveDirection = 1;
else if (direction_x < 0 && direction_y < 0)
curveDirection = 1;
else
curveDirection = -1;
}
// We calculate the correct speed at which to draw the curve.
// Since it's a quarter of a circle, we use pi.
range = ((radius * 2) * Math.PI) / 4;
line.curve += (speed * (90 / range)) * curveDirection;
if (direction == "x") {
if (line.curve >= 90 &&(!(direction_x > 0 && direction_y > 0) ||
(direction_x < 0 && direction_y < 0))) line.curve = 90;
if (line.curve <= -90 &&((direction_x > 0 && direction_y > 0) ||
(direction_x < 0 && direction_y < 0))) line.curve = -90;
line_xy = line._drawArc(lineCaret_x, 0,
radius, radius, line.curve, curveStart);
} else if (direction == "y") {
if (line.curve >= 90 &&(!(direction_x > 0 && direction_y < 0) ||
(direction_x < 0 && direction_y > 0))) line.curve = 90;
if (line.curve <= -90 &&((direction_x > 0 && direction_y < 0) ||
(direction_x < 0 && direction_y > 0))) line.curve = -90;
line_xy = line._drawArc(0, lineCaret_y,
radius, radius, line.curve, curveStart);
}
if (line.curve == 90 || line.curve == -90) {
lineCaret_x = line_xy.x;
lineCaret_y = line_xy.y;
line.step = 3;
}
}
if (line.step == 3) {
// Finally, draw the remainder.
line.moveTo(lineCaret_x, lineCaret_y);
if (direction == "x") {
lineCaret_y += speed * direction_y;
if (((direction_y < 0) && lineCaret_y <= end_y) ||
((direction_y > 0) && lineCaret_y >= end_y)) {
lineCaret_y = end_y;
line.step = 4;
}
} else if (direction == "y") {
lineCaret_x += speed * direction_x;
if (((direction_x < 0) && lineCaret_x <= end_x) ||
((direction_x > 0) && lineCaret_x >= end_x)) {
lineCaret_x = end_x;
line.step = 4;
}
}
line.lineTo(lineCaret_x, lineCaret_y);
}
if (line.step == 4) {
line.finished = true;
delete line.onEnterFrame;
}
line.glcaret_x = lineCaret_x + start_x;
line.glcaret_y = lineCaret_y + start_y;
line.caret_x = lineCaret_x;
line.caret_y = lineCaret_y;
};
// We also manually run the onEnterFrame function once to ensure there
// is a minimal delay before it actually begins.
line.onEnterFrame();
return line;
};
// _drawArc function arc segment drawer.
// Usage:
// _drawarc(x, y, radius_x, radius_y, arc, startAngle)
// Parameters:
// x the current pen position coordinate x
// y the current pen position coordinate y
// radius_x the radius of the arc.
// radius_y a modifier for the radius to produce elliptical arcs
// arc the sweep of the arc
// startAngle starting angle in degrees
// Description:
// Prototype method for drawing arc segments. It should be used by
// another function rather than on its own (hence the underscore in the
// function name).
MovieClip.prototype._drawArc = function( x:Number,
y:Number,
radius_x:Number,
radius_y:Number,
arc:Number,
startAngle:Number):Object
{
if (arguments.length < 6) return;
if (radius_y == undefined) {
// In case there's no radius_y defined, make a circular arc.
radius_y = radius_x;
}
if (Math.abs(arc) > 360) {
// A value bigger than 360 would cause more to be drawn than would
// be visible.
arc = 360;
}
var sa, theta, ang, angm, segs, ax, ay, bx, by, cx, cy;
// Calculates how many segments are necessary for our arc.
segs = Math.ceil(Math.abs(arc) / 45);
// Calculate the sweep of each segment.
sa = arc / segs;
// The math requires radians rather than degrees; convert.
theta = -(sa / 180) * Math.PI;
ang = -(startAngle / 180) * Math.PI;
// Determine the starting points (ax, ay) relative to the specified x, y.
ax = x-Math.cos(ang) * radius_x;
ay = y-Math.sin(ang) * radius_y;
if (segs > 0) {
for (var a = 0; a < segs; a++) {
ang += theta;
// Determine the angle halfway between the last and new one.
angm = ang - (theta / 2);
// Determine the end point.
bx = ax + Math.cos(ang) * radius_x;
by = ay + Math.sin(ang) * radius_y;
// Determine the control point.
cx = ax + Math.cos(angm) * (radius_x / Math.cos(theta / 2));
cy = ay + Math.sin(angm) * (radius_y / Math.cos(theta / 2));
this.curveTo(cx, cy, bx, by);
}
}
// Finally, return the end point for further processing.
return {x: bx, y: by};
};