Posted in AIR, Adobe, Flex, General
12/7 2009

Double-headed Arrows with GraphicsUtil

I recently ran across this awesome utility class, GraphicsUtil, created by Noel Billig. It allows the ability to draw lines with an arrow on one end using the drawing API in AS3. This proved useful for a project I was working on. In addition to its default behavior however, I needed the utility class to provide the ability to draw lines with arrows on both ends. Well thanks to Noel’s clean code, I was able to easily modify the base class to do just that by adding the following static method:

/**
 * Draws a double-headed arrow. Pass in ArrowStyle
 * objects for both arrows to override the default settings.
 * @param graphics
 * @param start
 * @param end
 * @param startStyle
 * @param endStyle
 */
public static function drawArrows( graphics:Graphics,
        start:Point,end:Point,
        startStyle:Object=null,
        endStyle:Object=null ):void
{
	// variables used for arrow 1
	var startArrowStyle:ArrowStyle;
	var startHalfWidth:Number;
	var vect1:Point;
	var startNorm:Point;
	var start1:Point;
	var start2:Point;
	var end1:Point;
	var end2:Point;
	var startHeadPnt:Point;
	var startHeadPntNorm:Point;
	var startEdge1:Point;
	var startEdge2:Point;
	var startShaftCenter:Point;
	var startInter1:Point;
	var startInter2:Point;
	var startEdgeCenter:Point;
	var startEdgeNorm:Point;
	var startEdgeCntrl1:Point;
	var startEdgeCntrl2:Point;
 
	// variables used for arrow 2
	var endArrowStyle:ArrowStyle;
	var endHalfWidth:Number;
	var vect2:Point;
	var endNorm:Point;
	var end3:Point;
	var end4:Point;
	var start3:Point;
	var start4:Point;
	var endHeadPnt:Point;
	var endHeadPntNorm:Point;
	var endEdge1:Point;
	var endEdge2:Point;
	var endShaftCenter:Point;
	var endInter1:Point;
	var endInter2:Point;
	var endEdgeCenter:Point;
	var endEdgeNorm:Point;
	var endEdgeCntrl1:Point;
	var endEdgeCntrl2:Point;
 
	if (start.equals(end)) return;
 
	///////////////////////////////// start arrow config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	if (startStyle == null)
	{
		startArrowStyle = new ArrowStyle();
	}
	else if (startStyle is ArrowStyle)
	{
		startArrowStyle = startStyle as ArrowStyle;
	}
	else
	{
		startArrowStyle = new ArrowStyle( startStyle );
	}
 
	vect1 = end.subtract( start );
	startHalfWidth = (startArrowStyle.headWidth != -1) ? startArrowStyle.headWidth / 2 : startArrowStyle.headLength / 2; 
 
	//Figure out the line start/end points
	startNorm = new Point( vect1.y, -vect1.x );
	startNorm.normalize( startArrowStyle.shaftThickness/ 2 );
	start1 = start.add( startNorm );
	start2 = start.subtract( startNorm );
	end1 = end.add( startNorm );
	end2 = end.subtract( startNorm );
 
	//figure out where the arrow head starts
	startHeadPnt = vect1.clone();
	startHeadPnt.normalize( startHeadPnt.length - startArrowStyle.headLength );
	startHeadPnt = startHeadPnt.add( start );
 
	//calculate the arrowhead corners
	startHeadPntNorm = startNorm.clone();
	startHeadPntNorm.normalize( startHalfWidth );
	startEdge1 = startHeadPnt.add( startHeadPntNorm );
	startEdge2 = startHeadPnt.subtract( startHeadPntNorm );
 
	//Figure out where the arrow connects the the shaft, then calc the intersections
	startShaftCenter = Point.interpolate( end, startHeadPnt, startArrowStyle.shaftPosition );
	startInter1 = GeomUtil.getLineIntersection( start1, end1, startShaftCenter, startEdge1 );
	startInter2 = GeomUtil.getLineIntersection( start2, end2, startShaftCenter, startEdge2 );
 
	//Figure out the control points
	startEdgeCenter = Point.interpolate( end, startHeadPnt, startArrowStyle.edgeControlPosition );
	startEdgeNorm = startNorm.clone();
	startEdgeNorm.normalize( startHalfWidth * startArrowStyle.edgeControlSize );
	startEdgeCntrl1 = startEdgeCenter.add( startEdgeNorm );
	startEdgeCntrl2 = startEdgeCenter.subtract( startEdgeNorm );
 
	///////////////////////////////// end arrow config \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	if (endStyle == null)
	{
		endArrowStyle = new ArrowStyle();
	}
	else if (endStyle is ArrowStyle)
	{
		endArrowStyle = endStyle as ArrowStyle;
	}
	else
	{
		endArrowStyle = new ArrowStyle( endStyle );
	}
 
	vect2 = start.subtract( end );
	endHalfWidth = (endArrowStyle.headWidth != -1) ? endArrowStyle.headWidth / 2 : endArrowStyle.headLength / 2;
 
	//Figure out the line start/end points
	endNorm = new Point( vect2.y, -vect2.x );
	endNorm.normalize( endArrowStyle.shaftThickness / 2 );
	start3 = start.add( endNorm );
	start4 = start.subtract( endNorm );
	end3 = end.add( endNorm );
	end4 = end.subtract( endNorm );
 
	//figure out where the arrow head starts
	endHeadPnt = vect2.clone();
	endHeadPnt.normalize( endHeadPnt.length - endArrowStyle.headLength );
	endHeadPnt = endHeadPnt.add( end );
 
	//calculate the arrowhead corners
	endHeadPntNorm = endNorm.clone();
	endHeadPntNorm.normalize( endHalfWidth );
	endEdge1 = endHeadPnt.add( endHeadPntNorm );
	endEdge2 = endHeadPnt.subtract( endHeadPntNorm );
 
	//Figure out where the arrow connects the the shaft, then calc the intersections
	endShaftCenter = Point.interpolate( start, endHeadPnt, endArrowStyle.shaftPosition );
	endInter1 = GeomUtil.getLineIntersection( end3, start3, endShaftCenter, endEdge1 );
	endInter2 = GeomUtil.getLineIntersection( end4, start4, endShaftCenter, endEdge2 );
 
	//Figure out the control points
	endEdgeCenter = Point.interpolate( start, endHeadPnt, endArrowStyle.edgeControlPosition );
	endEdgeNorm = endNorm.clone();
	endEdgeNorm.normalize( endHalfWidth * endArrowStyle.edgeControlSize );
	endEdgeCntrl1 = endEdgeCenter.add( endEdgeNorm );
	endEdgeCntrl2 = endEdgeCenter.subtract( endEdgeNorm );
 
	///////////////////////////////// draw the graphics \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
	// draw 1st arrow
	graphics.moveTo( start1.x, start2.y );
	graphics.lineTo( startInter1.x, startInter1.y );
	graphics.lineTo( startEdge1.x, startEdge1.y );
	graphics.curveTo( startEdgeCntrl1.x, startEdgeCntrl1.y, end.x, end.y );
	graphics.curveTo( startEdgeCntrl2.x, startEdgeCntrl2.y, startEdge2.x, startEdge2.y );
	graphics.lineTo( startInter2.x, startInter2.y );
	graphics.lineTo( start2.x, start2.y );
	graphics.lineTo( start1.x, start1.y );
 
	// draw 2nd arrow
	graphics.moveTo( end3.x, end4.y );
	graphics.lineTo( endInter1.x, endInter1.y );
	graphics.lineTo( endEdge1.x, endEdge1.y );
	graphics.curveTo( endEdgeCntrl1.x, endEdgeCntrl1.y, start.x, start.y );
	graphics.curveTo( endEdgeCntrl2.x, endEdgeCntrl2.y, endEdge2.x, endEdge2.y );
	graphics.lineTo( endInter2.x, endInter2.y );
	graphics.lineTo( end4.x, end4.y );
	graphics.lineTo( end3.x, end3.y );
}

Those are the basics. It can be tweaked to taste, etc. Kudos to Noel for such a useful class… :)

This Post tags: ,

 

USER COMMENTS

Track comments via RSS 2.0 feed. Feel free to post the comment, or trackback from your web site.

Currently there are no comments related to article "Double-headed Arrows with GraphicsUtil".

Get Adobe Flash playerPlugin by wpburn.com wordpress themes