// ShapeBuilder - simple ActionScript drawing API functions // Version 0.15 // // Copyright (C) 2006, Michiel Sikma // 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}; };