Tangents between two circles
/**
* Copyright yonatan ( http://wonderfl.net/user/yonatan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/jVmp
*/
// forked from 0xABCDEF's drawBridge
package
{
import com.bit101.components.HSlider;
import com.bit101.components.Label;
import flash.display.GraphicsPath;
import flash.display.GraphicsPathWinding;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
[ SWF( frameRate = "60" ) ]
public class DrawBridge extends Sprite
{
private const MAX_RADIUS:Number = 100;
private var startRadius:Number;
private var endRadius:Number;
private var startSlider:HSlider;
private var endSlider:HSlider;
private var startControl:PointControl;
private var endControl:PointControl;
public function DrawBridge()
{
startRadius = 10;
endRadius = 100;
new Label( this, 20, 10, "start" );
new Label( this, 20, 30, "end" );
startSlider = new HSlider( this, 50, 15, function():void {
startRadius = startSlider.value * MAX_RADIUS / 100;
startControl.radius = startRadius;
startControl.draw();
} );
startSlider.value = startRadius / MAX_RADIUS * 100;
endSlider = new HSlider( this, 50, 35, function():void {
endRadius = endSlider.value * MAX_RADIUS / 100;
endControl.radius = endRadius;
endControl.draw();
} );
endSlider.value = endRadius / MAX_RADIUS * 100;
addEventListener( Event.ENTER_FRAME, ENTER_FRAME );
startControl = addChild( new PointControl( 0xFF0000, startRadius ) ) as PointControl;
startControl.x = 100;
startControl.y = 200;
endControl = addChild( new PointControl( 0x0000FF, endRadius ) ) as PointControl;
endControl.x = 300;
endControl.y = 200;
}
private function ENTER_FRAME( e:Event ):void
{
startControl.updatePoint();
endControl.updatePoint();
graphics.clear();
graphics.beginFill( 0 );
drawBridge( startControl.point, endControl.point, startRadius, endRadius );
graphics.endFill();
}
private function drawBridge(
start:Point,
end:Point,
startRadius:Number,
endRadius:Number ):void
{
var t:Array = getTangents(start.x, start.y, startRadius, end.x, end.y, endRadius);
if(t) {
graphics.moveTo( t[0][0].x, t[0][0].y );
graphics.lineTo( t[0][1].x, t[0][1].y );
graphics.lineTo( t[1][1].x, t[1][1].y );
graphics.lineTo( t[1][0].x, t[1][0].y );
}
}
// from http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Tangents_between_two_circles
private function getTangents(x1:Number, y1:Number, r1:Number, x2:Number, y2:Number, r2:Number):Array {
var dsq:Number = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
if (dsq <= (r1-r2)*(r1-r2)) return null;
var d:Number = Math.sqrt(dsq);
var vx:Number = (x2 - x1) / d;
var vy:Number = (y2 - y1) / d;
var res:Array = [];
var i:int = 0;
// Let A, B be the centers, and C, D be points at which the tangent
// touches first and second circle, and n be the normal vector to it.
//
// We have the system:
// n * n = 1 (n is a unit vector)
// C = A + r1 * n
// D = B +/- r2 * n
// n * CD = 0 (common orthogonality)
//
// n * CD = n * (AB +/- r2*n - r1*n) = AB*n - (r1 -/+ r2) = 0, <=>
// AB * n = (r1 -/+ r2), <=>
// v * n = (r1 -/+ r2) / d, where v = AB/|AB| = AB/d
// This is a linear equation in unknown vector n.
for (var sign1:int = +1; sign1 >= -1; sign1 -= 2) {
var c:Number = (r1 - sign1 * r2) / d;
// Now we're just intersecting a line with a circle: v*n=c, n*n=1
if (c*c > 1.0) continue;
var h:Number = Math.sqrt(Math.max(0.0, 1.0 - c*c));
for (var sign2:int = +1; sign2 >= -1; sign2 -= 2) {
var nx:Number = vx * c - sign2 * h * vy;
var ny:Number = vy * c + sign2 * h * vx;
var a:Array = res[i++] = [];
a[0] = new Point(x1 + r1 * nx, y1 + r1 * ny);
a[1] = new Point(x2 + sign1 * r2 * nx, y2 + sign1 * r2 * ny);
}
}
return res;
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
internal class PointControl extends Sprite
{
public var point:Point;
public var color:uint;
public var radius:Number;
public function PointControl( color:uint, radius:Number )
{
this.point = new Point;
this.color = color;
this.radius = radius;
draw();
addEventListener( MouseEvent.MOUSE_DOWN, MOUSE_DOWN );
}
public function draw():void
{
graphics.clear();
graphics.beginFill( color );
graphics.drawCircle( 0, 0, radius );
graphics.endFill();
}
private function MOUSE_DOWN( e:MouseEvent ):void
{
startDrag();
stage.addEventListener( MouseEvent.MOUSE_UP, MOUSE_UP );
}
private function MOUSE_UP( e:MouseEvent ):void
{
stopDrag();
stage.removeEventListener( MouseEvent.MOUSE_UP, MOUSE_UP );
}
public function updatePoint():void
{
point.x = x;
point.y = y;
}
public function updateControl():void
{
x = point.x;
y = point.y;
}
}