A client of mine wanted to create an interactive teacher animation that pointed to a point on a chart that could be calculated as the result of a user quiz. They were stuck though as creating a realistic elbow movement that points to the desired point from a programmatic perspective is actually harder than it seems as most teachers arms tend to have elbows. The solution took some time to come up with although simple enough in the end after a bit of geometric research. The first step to creating the animation was to calculate mathematically the intersections of two circles.
Finding the intersection of two circles appears complicated although in actual fact it is just a matter of unraveling Pythagoras' Theorem a few times. Pythagoras' Theorem states that the sum of square of the both straight sides of any right angled triangle is equal to the square of the diagonal.
ie. a2=b2+c2
Here is how you use it to find the intersection points of a circle with Actionscript:
1) Start by creating a couple of draggable circles and some hidden dots that will be used to indicate where the intersections are:
package { import flash.display.Sprite; import flash.geom.Point; import flash.events.Event; import flash.events.MouseEvent; public class FindIntersection extends Sprite { public var circle1Radius:Number = 40; public var circle2Radius:Number = 70; public var circle1:Sprite = drawCircle(circle1Radius); public var circle2:Sprite = drawCircle(circle2Radius); public var dot1:Sprite = drawDot(); public var dot2:Sprite = drawDot(); function FindIntersection() { var origin:Point = new Point(stage.stageWidth/2, stage.stageHeight/2); circle1Radius = 40; circle2Radius = 70; circle1 = drawCircle(circle1Radius); circle2 = drawCircle(circle2Radius); dot1 = drawDot(); dot2 = drawDot(); circle1.x = origin.x - 10; circle1.y = origin.y; circle2.x = origin.x + 10; circle2.y = origin.y; addChild(circle1); addChild(circle2); addChild(dot1); addChild(dot2); } function drawCircle(rad:Number):Sprite { var c:Sprite = new Sprite(); c.graphics.lineStyle(1,0x000000); c.graphics.beginFill(0xBBBBBB,.1); c.graphics.drawCircle(0,0,rad); c.graphics.endFill(); c.graphics.moveTo(-5,0); c.graphics.lineTo(5,0); c.graphics.moveTo(0,-5); c.graphics.lineTo(0,5); c.addEventListener(MouseEvent.MOUSE_DOWN, onDown); c.addEventListener(MouseEvent.MOUSE_UP, onUp); return c; } function drawDot() { var d:Sprite = new Sprite(); d.graphics.beginFill(0xFF0000); d.graphics.drawCircle(0,0,2); d.graphics.endFill(); d.visible = false; return d; } function onDown(e:Event):void { var circle:Sprite = e.target as Sprite; circle.startDrag(); } function onUp(e:Event):void { var circle:Sprite = e.target as Sprite; circle.stopDrag(); } } }
2) Next add an enterframe handler that checks for a collision by calculating the distance between the two circles. If the distance is greater than the sum of the radiae of both circles there is no intersection. Also if the distance is less than the absolute difference between the radiae then there is no solution as one circle is contained within the other.
function enterFrameHandler(e:Event):void { // Set up variables var r0:Number = circle1Radius; var r1:Number = circle2Radius; // Point x and y coords var p0x:Number = circle1.x; var p0y:Number = circle1.y; var p1x:Number = circle2.x; var p1y:Number = circle2.y; //First calculate the distance d between the center of the circles. d = ||P1 - P0||. var dx:Number = p0x - p1x; var dy:Number = p0y - p1y; var d:Number = Math.sqrt(dx*dx + dy*dy); //If d > r0 + r1 then there are no solutions, the circles are separate. //If d < |r0 - r1| then there are no solutions because one circle is contained within the other. if ((d > r0 + r1) || (d < Math.abs(r0 - r1))) { dot1.visible = false; dot2.visible = false; return; } }

The way we calculate distance is by using Pythagoras' formula by determining distance as a hypotenuse of a right angled triangle calculated as the square root of the square of the x-difference plus the square of the y-difference. ie.
d=√( dx2+dy2 )
3) Now comes something slightly trickier. Consider the following diagram:

Again using Pythagoras we can say that:
a2+ h2= r02 is true for the red triangle and b2+ h2= r12 is true for the green triangle.
Now knowing that d = a + b we can then use a bit of algebra to solve for a in terms of r0, r1 and d (I can show the working here if you want just leave a comment or send me a message):
a = (r02 - r12 + d2 ) / (2 d)
Which we can write in actionscript as:
var a:Number = ( r0*r0 - r1*r1 + d*d ) / (2*d);
Have a look at the following diagram:

Now since P2 is along the line distance d and the x and y coordinates are relative to a/d therefore:
var p2x:Number = p0x + a * ( p1x - p0x ) / d; var p2y:Number = p0y + a * ( p1y - p0y ) / d;
Since we know that r02 = h2+ a2 and now that we have the value of a we can get h using pythagoras yet again:
var h:Number = Math.sqrt(r0*r0 - a*a);
Now all that is left to do is project the positive h point and the negative h point:
var d1x:Number = p2x + h * ( p1y - p0y ) / d; var d1y:Number = p2y - h * ( p1x - p0x ) / d; var d2x:Number = p2x - h * ( p1y - p0y ) / d; var d2y:Number = p2y + h * ( p1x - p0x ) / d;
Now we have the points of intersection, our file should look something like this:
Here is the source code.
Soon I plan to write about how to make an actual arm move based on this code but that is for another article.
One Comment
thank you for explaining this so well
One Trackback
[...] This post was mentioned on Twitter by Activetuts+ and rudi yardley, Henrique Vilela. Henrique Vilela said: How to calculate the intersection of two circles mathematically in #Actionscript. http://is.gd/6AI61 [...]