Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
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></strong>required</strong> * @param y1 Number starting position on y axis - <strong></strong>required</strong> * @param x2 Number finishing position on x axis - <strong></strong>required</strong> * @param y2 Number finishing position on y axis - <strong></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@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@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@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@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@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; } } }
This paste will be private.
From the Design Piracy series on my blog: