In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Tangents between two circles

Get Adobe Flash player
by yonatan 13 Nov 2012
    Embed
/**
 * 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;
    }
}