Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
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;
		}
	}
}