/ Published in: ActionScript 3
Credit for this goes to Adobe, Ric Ewing, Kevin Williams, Aden Forshaw and Sidney de Koning.
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
package nl.funkymonkey.drawing { import flash.display.*; /** * based on source code found at: * http://www.macromedia.com/devnet/mx/flash/articles/adv_draw_methods.html * * @author Ric Ewing - version 1.4 - 4.7.2002 * @author Kevin Williams - version 2.0 - 4.7.2005 * @author Aden Forshaw - Version AS3 - 19.4.2010 * @author Sidney de Koning - Version AS3 - 20.4.2010 - errors/correct datatypes/optimized math operations * * Usage: * var s : Shape = new Shape( ); // Or Sprite of MovieClip or any other Class that makes use of the Graphics class * * // Draw an ARC * s.graphics.lineStyle( 4, 0xE16606 ); * DrawingShapes.drawArc( s.graphics, 50, 50, 10, 150, 60 ); * * // Draw an BURST * s.graphics.lineStyle( 3, 0x000000 ); * DrawingShapes.drawBurst( s.graphics, 80, 60, 3, 15, 6, 27 ); * * // Draw an DASHED-LINE like so - - - - * s.graphics.lineStyle( 1, 0x3C3C39 ); * DrawingShapes.drawDash( s.graphics, 120, 60, 150, 80, 2, 2 ); * * // Draw an GEAR * s.graphics.lineStyle( 3, 0xE16606 ); * DrawingShapes.drawGear( s.graphics, 200, 60, 13, 31, 26, 0, 7, 13 ); * * // Draw a POLYGON * s.graphics.lineStyle( 3, 0x0074B9 ); * DrawingShapes.drawPolygon( s.graphics, 270, 60, 7, 30, 45 ); * * // Draw a STAR * s.graphics.lineStyle( 2, 0x000000 ); * DrawingShapes.drawStar( s.graphics, 340, 60, 18, 24, 19, 27 ); * * // Draw an WEDGE - good for pie charts or pacmans * s.graphics.lineStyle( 2, 0xFFCC00 ); * DrawingShapes.drawWedge( s.graphics, 400, 60, 30, 309, 209 ); * * // Draw a LINE * s.graphics.lineStyle( 2, 0x0074B9 ); * DrawingShapes.drawLine( s.graphics, 440, 85, 30, DrawingShapes.VERTICAL_LINE ); * * addChild( s ); */ public class DrawingShapes { public static const HORIZONTAL_LINE:String = "DrawingShapes.horizontal"; public static const VERTICAL_LINE:String = "DrawingShapes.vertical"; public function DrawingShapes() { throw new ArgumentError("The DrawingShapes Class cannot be instanicated."); } /** * drawDash * Draws a dashed line from the point x1,y1 to the point x2,y2 * * @param target Graphics the Graphics Class on which the dashed line will be drawn. * @param x1 Number starting position on x axis - <strong>required</strong> * @param y1 Number starting position on y axis - <strong>required</strong> * @param x2 Number finishing position on x axis - <strong>required</strong> * @param y2 Number finishing position on y axis - <strong>required</strong> * @param dashLength [optional] Number the number of pixels long each dash * will be. Default = 5 * @param spaceLength [optional] Number the number of pixels between each * dash. Default = 5 */ public static function drawDash(target:Graphics, x1:Number, y1:Number, x2:Number, y2:Number, dashLength:Number=5, spaceLength:Number=5):void { var x:Number = x2 - x1; var y:Number = y2 - y1; var hyp:Number = Math.sqrt((x) * (x) + (y) * (y)); var units:Number = hyp / (dashLength + spaceLength); var dashSpaceRatio:Number = dashLength / (dashLength + spaceLength); var dashX:Number = (x / units) * dashSpaceRatio; var spaceX:Number = (x / units) - dashX; var dashY:Number = (y / units) * dashSpaceRatio; var spaceY:Number = (y / units) - dashY; target.moveTo(x1, y1); while (hyp > 0) { x1 += dashX; y1 += dashY; hyp -= dashLength; if (hyp < 0) { x1 = x2; y1 = y2; } target.lineTo(x1, y1); x1 += spaceX; y1 += spaceY; target.moveTo(x1, y1); hyp -= spaceLength; } target.moveTo(x2, y2); } /** * Draws an arc from the starting position of x,y. * * @param target the Graphics Class that the Arc is drawn on. * @param x x coordinate of the starting pen position * @param y y coordinate of the starting pen position * @param radius radius of Arc. * @param arc = sweep of the arc. Negative values draw clockwise. * @param startAngle = [optional] starting offset angle in degrees. * @param yRadius = [optional] y radius of arc. if different than * radius, then the arc will draw as the arc of an oval. * default = radius. * * Based on mc.drawArc by Ric Ewing. * the version by Ric assumes that the pen is at x:y before this * method is called. I explictily move the pen to x:y to be * consistent with the behaviour of the other methods. */ public static function drawArc(target:Graphics, x:Number, y:Number, radius:Number, arc:Number, startAngle:Number=0, yRadius:Number=0):void { if (arguments.length < 5) { throw new ArgumentError("DrawingShapes.drawArc() - too few parameters, need atleast 5."); return; } // if startAngle is undefined, startAngle = 0 if (startAngle == 0) { startAngle = 0; } // if yRadius is undefined, yRadius = radius if (yRadius == 0) { yRadius = radius; } // Init vars var segAngle:Number, theta:Number, angle:Number, angleMid:Number, segs:Number, ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number; // no sense in drawing more than is needed :) if (DrawingShapes.abs(arc) > 360) { arc = 360; } // Flash uses 8 segments per circle, to match that, we draw in a maximum // of 45 degree segments. First we calculate how many segments are needed // for our arc. segs = DrawingShapes.ceil(DrawingShapes.abs(arc) / 45); // Now calculate the sweep of each segment segAngle = arc / segs; // The math requires radians rather than degrees. To convert from degrees // use the formula (degrees/180)*Math.PI to get radians. theta = -(segAngle / 180) * Math.PI; // convert angle startAngle to radians angle = -(startAngle / 180) * Math.PI; // find our starting points (ax,ay) relative to the secified x,y ax = x - Math.cos(angle) * radius; ay = y - Math.sin(angle) * yRadius; // if our arc is larger than 45 degrees, draw as 45 degree segments // so that we match Flash's native circle routines. if (segs > 0) { target.moveTo(x, y); // Loop for drawing arc segments for (var i:int = 0; i < segs; ++i) { // increment our angle angle += theta; // find the angle halfway between the last angle and the new angleMid = angle - (theta / 2); // calculate our end point bx = ax + Math.cos(angle) * radius; by = ay + Math.sin(angle) * yRadius; // calculate our control point cx = ax + Math.cos(angleMid) * (radius / Math.cos(theta / 2)); cy = ay + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2)); // draw the arc segment target.curveTo(cx, cy, bx, by); } } } /** * draws pie shaped wedges. Could be employeed to draw pie charts. * * @param target the Graphics on which the wedge is to be drawn. * @param x x coordinate of the center point of the wedge * @param y y coordinate of the center point of the wedge * @param radius the radius of the wedge * @param arc the sweep of the wedge. negative values draw clockwise * @param startAngle the starting angle in degrees * @param yRadius [optional] the y axis radius of the wedge. * If not defined, then yRadius = radius. * * based on mc.drawWedge() - by Ric Ewing (ric at formequalsfunction.com) - version 1.4 - 4.7.2002 */ public static function drawWedge(target:Graphics, x:Number, y:Number, radius:Number, arc:Number, startAngle:Number=0, yRadius:Number=0):void { // if yRadius is undefined, yRadius = radius if (yRadius == 0) { yRadius = radius; } // move to x,y position target.moveTo(x, y); // if yRadius is undefined, yRadius = radius if (yRadius == 0) { yRadius = radius; } // Init vars var segAngle:Number, theta:Number, angle:Number, angleMid:Number, segs:Number, ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number; // limit sweep to reasonable numbers if (DrawingShapes.abs(arc) > 360) { arc = 360; } // Flash uses 8 segments per circle, to match that, we draw in a maximum // of 45 degree segments. First we calculate how many segments are needed // for our arc. segs = DrawingShapes.ceil(DrawingShapes.abs(arc) / 45); // Now calculate the sweep of each segment. segAngle = arc / segs; // The math requires radians rather than degrees. To convert from degrees // use the formula (degrees/180)*Math.PI to get radians. theta = -(segAngle / 180) * Math.PI; // convert angle startAngle to radians angle = -(startAngle / 180) * Math.PI; // draw the curve in segments no larger than 45 degrees. if (segs > 0) { // draw a line from the center to the start of the curve ax = x + Math.cos(startAngle / 180 * Math.PI) * radius; ay = y + Math.sin(-startAngle / 180 * Math.PI) * yRadius; target.lineTo(ax, ay); // Loop for drawing curve segments for (var i:int = 0; i < segs; ++i) { angle += theta; angleMid = angle - (theta / 2); bx = x + Math.cos(angle) * radius; by = y + Math.sin(angle) * yRadius; cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2)); cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2)); target.curveTo(cx, cy, bx, by); } // close the wedge by drawing a line to the center target.lineTo(x, y); } } /** * start draws a star shaped polygon. * * <blockquote>Note that the stars by default 'point' to * the right. This is because the method starts drawing * at 0 degrees by default, putting the first point to * the right of center. Negative values for points * draws the star in reverse direction, allowing for * knock-outs when used as part of a mask.</blockquote> * * @param target the Graphics that the star is drawn on * @param x x coordinate of the center of the star * @param y y coordinate of the center of the star * @param points the number of points on the star * @param innerRadius the radius of the inside angles of the star * @param outerRadius the radius of the outside angles of the star * @param angle [optional] the offet angle that the start is rotated * * based on mc.drawStar() - by Ric Ewing (ric at formequalsfunction.com) - version 1.4 - 4.7.2002 */ public static function drawStar(target:Graphics, x:Number, y:Number, points:uint, innerRadius:Number, outerRadius:Number, angle:Number=0):void { // check that points is sufficient to build polygon if (points <= 2) { throw ArgumentError("DrawingShapes.drawStar() - parameter 'points' needs to be atleast 3"); return; } if (points > 2) { // init vars var step:Number, halfStep:Number, start:Number, n:Number, dx:Number, dy:Number; // calculate distance between points step = (Math.PI * 2) / points; halfStep = step / 2; // calculate starting angle in radians start = (angle / 180) * Math.PI; target.moveTo(x + (Math.cos(start) * outerRadius), y - (Math.sin(start) * outerRadius)); // draw lines for (n = 1; n <= points; ++n) { dx = x + Math.cos(start + (step * n) - halfStep) * innerRadius; dy = y - Math.sin(start + (step * n) - halfStep) * innerRadius; target.lineTo(dx, dy); dx = x + Math.cos(start + (step * n)) * outerRadius; dy = y - Math.sin(start + (step * n)) * outerRadius; target.lineTo(dx, dy); } } } /** * a method for creating polygon shapes. Negative values will draw * the polygon in reverse direction. Negative drawing may be useful * for creating knock-outs in masks. * * @param target the Graphics that the polygon is to be drawn on * @param x x coordinate of the center of the polygon * @param y y coordinate of the center of the polygon * @param sides the number of sides (must be > 2) * @param radius the radius from the center point to the points * on the polygon * @param angle [optional] the starting offset angle (degrees) from * 0. Default = 0 * * based on mc.drawPoly() - by Ric Ewing (ric at formequalsfunction.com) - version 1.4 - 4.7.2002 */ public static function drawPolygon(target:Graphics, x:Number, y:Number, sides:uint, radius:Number, angle:Number=0):void { // check that sides is sufficient to build if (sides <= 2) { throw ArgumentError("DrawingShapes.drawPolygon() - parameter 'sides' needs to be atleast 3"); return; } if (sides > 2) { // init vars var step:Number, start:Number, n:Number, dx:Number, dy:Number; // calculate span of sides step = (Math.PI * 2) / sides; // calculate starting angle in radians start = (angle / 180) * Math.PI; target.moveTo(x + (Math.cos(start) * radius), y - (Math.sin(start) * radius)); // draw the polygon for (n = 1; n <= sides; ++n) { dx = x + Math.cos(start + (step * n)) * radius; dy = y - Math.sin(start + (step * n)) * radius; target.lineTo(dx, dy); } } } /** * Burst is a method for drawing star bursts. If you've ever worked * with an advertising department, you know what they are ;-) * Clients tend to want them, Developers tend to hate them... * * @param target Graphics where the Burst is to be drawn. * @param x x coordinate of the center of the burst * @param y y coordinate of the center of the burst * @param sides number of sides or points * @param innerRadius radius of the indent of the curves * @param outerRadius radius of the outermost points * @param angle [optional] starting angle in degrees. (defaults to 0) * * based on mc.drawBurst() - by Ric Ewing (ric at formequalsfunction.com) - version 1.4 - 4.7.2002 */ public static function drawBurst(target:Graphics, x:Number, y:Number, sides:uint, innerRadius:Number, outerRadius:Number, angle:Number=0):void { // check that sides is sufficient to build if (sides <= 2) { throw ArgumentError("DrawingShapes.drawBurst() - parameter 'sides' needs to be atleast 3"); return; } if (sides > 2) { // init vars var step:Number, halfStep:Number, qtrStep:Number, start:Number, n:Number, dx:Number, dy:Number, cx:Number, cy:Number; // calculate length of sides step = (Math.PI * 2) / sides; halfStep = step / 2; qtrStep = step / 4; // calculate starting angle in radians start = (angle / 180) * Math.PI; target.moveTo(x + (Math.cos(start) * outerRadius), y - (Math.sin(start) * outerRadius)); // draw curves for (n = 1; n <= sides; ++n) { cx = x + Math.cos(start + (step * n) - (qtrStep * 3)) * (innerRadius / Math.cos(qtrStep)); cy = y - Math.sin(start + (step * n) - (qtrStep * 3)) * (innerRadius / Math.cos(qtrStep)); dx = x + Math.cos(start + (step * n) - halfStep) * innerRadius; dy = y - Math.sin(start + (step * n) - halfStep) * innerRadius; target.curveTo(cx, cy, dx, dy); cx = x + Math.cos(start + (step * n) - qtrStep) * (innerRadius / Math.cos(qtrStep)); cy = y - Math.sin(start + (step * n) - qtrStep) * (innerRadius / Math.cos(qtrStep)); dx = x + Math.cos(start + (step * n)) * outerRadius; dy = y - Math.sin(start + (step * n)) * outerRadius; target.curveTo(cx, cy, dx, dy); } } } /** * draws a gear shape on the Graphics target. The gear position * is indicated by the x and y arguments. * * @param target Graphics on which the gear is to be drawn. * @param x x coordinate of the center of the gear * @param y y coordinate of the center of the gear * @param sides number of teeth on gear. (must be > 2) * @param innerRadius radius of the indent of the teeth. * @param outerRadius outer radius of the teeth. * @param angle = [optional] starting angle in degrees. Defaults to 0. * @param holeSides [optional] draw a polygonal hole with this many sides (must be > 2) * @param holeRadius [optional] size of hole. Default = innerRadius/3. * * based on mc.drawGear() - by Ric Ewing (ric at formequalsfunction.com) - version 1.4 - 4.7.2002 */ public static function drawGear(target:Graphics, x:Number, y:Number, sides:uint, innerRadius:Number=80, outerRadius:Number=4, angle:Number=0, holeSides:Number=2, holeRadius:Number=0):void { // check that sides is sufficient to build polygon if (sides <= 2) { throw ArgumentError("DrawingShapes.drawGear() - parameter 'sides' needs to be atleast 3"); return; } if (sides > 2) { // init vars var step:Number, qtrStep:Number, start:Number, n:Number, dx:Number, dy:Number; // calculate length of sides step = (Math.PI * 2) / sides; qtrStep = step / 4; // calculate starting angle in radians start = (angle / 180) * Math.PI; target.moveTo(x + (Math.cos(start) * outerRadius), y - (Math.sin(start) * outerRadius)); // draw lines for (n = 1; n <= sides; ++n) { dx = x + Math.cos(start + (step * n) - (qtrStep * 3)) * innerRadius; dy = y - Math.sin(start + (step * n) - (qtrStep * 3)) * innerRadius; target.lineTo(dx, dy); dx = x + Math.cos(start + (step * n) - (qtrStep * 2)) * innerRadius; dy = y - Math.sin(start + (step * n) - (qtrStep * 2)) * innerRadius; target.lineTo(dx, dy); dx = x + Math.cos(start + (step * n) - qtrStep) * outerRadius; dy = y - Math.sin(start + (step * n) - qtrStep) * outerRadius; target.lineTo(dx, dy); dx = x + Math.cos(start + (step * n)) * outerRadius; dy = y - Math.sin(start + (step * n)) * outerRadius; target.lineTo(dx, dy); } // This is complete overkill... but I had it done already. :) if (holeSides > 2) { step = (Math.PI * 2) / holeSides; target.moveTo(x + (Math.cos(start) * holeRadius), y - (Math.sin(start) * holeRadius)); for (n = 1; n <= holeSides; ++n) { dx = x + Math.cos(start + (step * n)) * holeRadius; dy = y - Math.sin(start + (step * n)) * holeRadius; target.lineTo(dx, dy); } } } } /** * draws a line between two points. Make it horizontal or vertical * * @param target Graphics on which the gear is to be drawn. * @param x x coordinate of the center of the gear * @param y y coordinate of the center of the gear * @param sides number of teeth on gear. (must be > 2) * * */ public static function drawLine(target:Graphics, x:Number, y:Number, length:Number, direction:String=DrawingShapes.HORIZONTAL_LINE):void { target.moveTo(x, y); switch (direction) { case DrawingShapes.HORIZONTAL_LINE: target.lineTo(length, y); break; case DrawingShapes.VERTICAL_LINE: target.moveTo(x, y); target.lineTo(x, length); break; } } /* * new abs function, about 25x faster than Math.abs */ private static function abs(value:Number):Number { return value < 0 ? -value : value; } /* * new ceil function about 75% faster than Math.ceil. */ private static function ceil(value:Number):Number { return (value % 1) ? int(value) + 1 : value; } } }