// you’re reading...

3D

Custom Shapes in FIVe3D: The Arc

The beauty of FIVe3D (other than its size, ease of use, and a few other things) is how easily extendable it is. For a previous experiment, I needed a way to draw an arc in 3D space. This isn’t something that FIVe3D provides, and in fact it was a little challenging to find the math I needed. However! Once I found it, I was able to easily use FIVe3D’s drawing API to put it into action.

The arc code, by the way, is from an ancient AS2 function called drawWedge() by Ric Ewing, which drew pizza-style wedges. Close enough. With a little elbow grease I managed to bend it to my will. Here’s an example of a bunch of arcs doing their thing:

And here is the code that draws the arc, which extends FIVe3D’s Drawing class. This could probably be cleaned up a bit, since I run through Ric’s arc code twice to create the thing; it wasn’t originally that way, but getting the fill on the inside of the arc proved more difficult than I originally thought.

/**
 * An extension of Mathieu Badimon's FIVe3D Drawing class.
 * This class draws an additional shape onto a FIVe3D Graphics3D instance.
 * This class includes code from by Ric Ewing.
 * 
 * @author 	Zack Jordan
 * 			{ P I X E L W E L D E R S }
 */
package com.pixelwelders.five3d
{
	import five3D.display.Graphics3D;
	import five3D.utils.Drawing;
 
	public class PXWDrawing extends Drawing
	{
 
		/**
		 * Draws a wedge shape onto a Graphics3D instance.
		 * 
		 * @param 	graphics		a Graphics3D instance on which to draw
		 * @param 	x				x position of the center of this wedge
		 * @param	y				y position of the center of this wedge
		 * @param	startAngle		the angle of one straight line of this wedge
		 * @param	arc				the angle (in degrees) of the total arc of this wedge
		 * @param	xRadius			the external radius along the x axis
		 * @param	yRadius			the external radius along the y axis
		 * @param	innerXRadius	the internal radius along the x axis
		 * @param	innerYRadius	the internal radius along the y axis
		 * @param	color			the color of the wedge fill
		 * @param	fillAlpha		the alpha value of the wedge fill
		 * 
		 * @return					nothing
		 */
		static public function drawWedge( 
			graphics:Graphics3D, 
			x:Number, 
			y:Number, 
			startAngle:Number, 
			arc:Number, 
			xRadius:Number, 
			yRadius:Number, 
			innerXRadius:Number, 
			innerYRadius:Number, 
			color:uint = 0xFF0000, 
			fillAlpha:Number = .5 
		): void
		{
			var segAngle	: Number
			var theta		: Number
			var angle		: Number
			var angleMid	: Number
			var segs		: Number
			var bx		: Number
			var by		: Number
			var cx		: Number
			var cy		: Number;
 
			segs = Math.ceil( Math.abs( arc ) / 45 );
			segAngle = arc / segs;
			theta = -( segAngle / 180 ) * Math.PI;
			angle = -( startAngle / 180 ) * Math.PI;
 
			graphics.lineStyle( 1, 0x000000, 1 );
			graphics.beginFill( color, fillAlpha );
			graphics.moveTo( 
				x + Math.cos( startAngle / 180 * Math.PI ) * innerXRadius,
				y + Math.sin( -startAngle/180 * Math.PI ) * innerYRadius 
			);
 
			// line 1
			graphics.lineTo( 
				x + Math.cos( startAngle / 180 * Math.PI ) * xRadius,
				y + Math.sin( -startAngle / 180 * Math.PI ) * yRadius 
			);
 
			// outer arc
			for ( var i:int = 0; i < segs; i++ ) {
				angle += theta;
				angleMid = angle - ( theta / 2 );
				bx = x + Math.cos( angle ) * xRadius;
				by = y + Math.sin( angle ) * yRadius;
				cx = x + Math.cos( angleMid ) * ( xRadius / Math.cos( theta / 2 ) );
				cy = y + Math.sin( angleMid ) * ( yRadius / Math.cos( theta / 2 ) );
				graphics.curveTo( cx, cy, bx, by );
			}
 
			// line 2
			graphics.lineTo( 
				x + Math.cos( ( startAngle + arc ) / 180 * Math.PI ) * innerXRadius, 
				y + Math.sin( -( startAngle + arc ) / 180 * Math.PI ) * innerYRadius 
			);
 
			theta = -( segAngle / 180 ) * Math.PI;
			angle = -( ( startAngle + arc ) / 180 ) * Math.PI;
 
			// inner arc
			for (var j:int = 0; j < segs; j++ ) {
				angle -= theta;
				angleMid = angle + ( theta / 2 );
				bx = x + Math.cos( angle ) * innerXRadius;
				by = y + Math.sin( angle ) * innerYRadius;
				cx = x + Math.cos( angleMid ) * ( innerXRadius / Math.cos( theta / 2 ) );
				cy = y + Math.sin( angleMid ) * ( innerYRadius / Math.cos( theta / 2 ) );
				graphics.curveTo( cx, cy, bx, by );
			}			
			graphics.endFill();			
		}
	}		
}

Another note on this experiment: the SWF is 16k. Holy crap.

Discussion

4 comments for “Custom Shapes in FIVe3D: The Arc”

  1. That’s pretty cool. It is strange that there is no better way to draw an arc. I still don’t know why Adobe sticks with Bezier curves rather than something nicer. It must be harder than I think to implement.

    Posted by Benjamin | June 21, 2008, 11:41 am
  2. […] engine used is Five3D + a Wedge class made by pixelwelders specially for this 3D engine […]

    Posted by PUBLIC SERVICE » Blog Archive » PI viewer | June 22, 2008, 6:53 pm
  3. Rational Bezier curves can exactly represent arcs. To be honest, I’m not sure if Adobe uses rational Bezier curves, or if they expose the weights, but Bezier curves in and of themselves are much more expressive than arcs.

    If you’re interested, I’d be happy to help.

    Posted by Tom Finnigan | July 19, 2008, 4:10 pm
  4. From what I’ve read, the Flash player uses multiple quadratic Bézier curves to approximate all curves. Apparently a quadratic curve is significantly faster to calculate and draw than a cubic curve (which is definitely more expressive). So I believe it’s a speed thing rather than a difficulty thing.

    With a quadratic curve, the most of a circle that I can draw appears to be 45 degrees. With a cubic curve, I think it would be possible to draw 90- and of course, it would be possible to draw much more interesting shapes. Then again, maybe I’m wrong; I’m no mathematician.

    Posted by Zack Jordan | July 19, 2008, 4:37 pm

Post a comment

Twitterpated