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

ソディの円 -Soddy circles-

Get Adobe Flash player
by Aquioux 06 Dec 2013
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/q6hQ
 */

package {
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#ffffff")]
    /**
     * ソディの円、もしくは、デカルトの円定理
     * @see http://aquioux.net/blog/?p=3823
     * @author YOSHIDA, Akio
     */
    public class Main extends Sprite {
        // 円
        private var circleA_:Circle;        // 正接円A
        private var circleB_:Circle;        // 正接円B
        private var circleC_:Circle;        // 正接円C
        private var circleD_:Circle;        // 内接円
        private var circleE_:Circle;        // 外接円
        private var draggingCircle_:Circle;    // ドラッグ中の正接円
        private var circleContainer_:Sprite;// 円のコンテナ

        // 線
        private var line_:Line;                // 正接円の中心を結ぶ直線


        /**
         * コンストラクタ
         */
        public function Main() {
            setup();
            draw();
        }

        /**
         * セットアップ
         */
        private function setup():void {
            // 円のコンテナ
            circleContainer_ = new Sprite();
            circleContainer_.mouseEnabled = false;
            addChild(circleContainer_);
            // 円の生成
            circleA_ = makeCircle(true,  0xff6666);
            circleB_ = makeCircle(true,  0x66ff66);
            circleC_ = makeCircle(true,  0x6666ff);
            circleD_ = makeCircle(false, 0x000000);
            circleE_ = makeCircle(false, 0x808080);
            // 正接円の配置
            const CENTER_X:Number = stage.stageWidth  / 2;
            const CENTER_Y:Number = stage.stageHeight / 2;
            // 極座標計算
            var radianA:Number = (-90 + Math.random() * 40 - 20) * Math.PI / 180;
            var radianB:Number = ( 30 + Math.random() * 40 - 20) * Math.PI / 180;
            var radianC:Number = (150 + Math.random() * 40 - 20) * Math.PI / 180;
            var lengthA:int = (Math.random() * 3 >> 0) * 10 + 100;
            var lengthB:int = (Math.random() * 3 >> 0) * 10 + 100;
            var lengthC:int = (Math.random() * 3 >> 0) * 10 + 100;
            circleA_.x = CENTER_X + Math.cos(radianA) * lengthA;
            circleA_.y = CENTER_Y + Math.sin(radianA) * lengthA;
            circleB_.x = CENTER_X + Math.cos(radianB) * lengthB;
            circleB_.y = CENTER_Y + Math.sin(radianB) * lengthB;
            circleC_.x = CENTER_X + Math.cos(radianC) * lengthC;
            circleC_.y = CENTER_Y + Math.sin(radianC) * lengthC;
            circleContainer_.addChild(circleD_);
            circleContainer_.addChild(circleE_);
            circleContainer_.addChild(circleA_);
            circleContainer_.addChild(circleB_);
            circleContainer_.addChild(circleC_);

            // 正接円の中心を結ぶ直線
            line_ = new Line();
            addChild(line_);
        }
        // 正接円の生成
        private function makeCircle(isDrag:Boolean, color:uint):Circle {
            var circle:Circle = new Circle(isDrag);
            circle.color = color;
            circle.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            return circle;
        }
        // マウスハンドラ
        private function mouseDownHandler(event:MouseEvent):void {
            draggingCircle_ = Circle(event.target);
            draggingCircle_.startDrag();
            stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP,   mouseUpHandler);
            stage.addEventListener(Event.MOUSE_LEAVE,     mouseUpHandler);
        }
        private function mouseMoveHandler(event:MouseEvent):void {
            draw();
        }
        private function mouseUpHandler(event:MouseEvent):void {
            draggingCircle_.stopDrag();
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            stage.removeEventListener(MouseEvent.MOUSE_UP,   mouseUpHandler);
            stage.removeEventListener(Event.MOUSE_LEAVE,     mouseUpHandler);
        }

        /**
         * 描画
         */
        private function draw():void {
            // 各円の計算
            Calculator.calcTangentCircles(circleA_, circleB_, circleC_);
            Calculator.calcSoddyCircles(circleA_, circleB_, circleC_, circleD_, circleE_);

            // 各円の描画
            circleA_.draw();
            circleB_.draw();
            circleC_.draw();
            circleD_.draw();
            circleE_.draw();

            // 線の描画
            line_.draw(circleA_, circleB_, circleC_);
        }
    }
}


//package {
    //import aquioux.math.Complex;
    //import aquioux.math.ComplexMath;
    /**
     * ソディの円、もしくは、デカルトの4円定理
     * http://en.wikipedia.org/wiki/Descartes'_theorem
     * http://mathworld.wolfram.com/DescartesCircleTheorem.html
     * http://mathworld.wolfram.com/SoddyCircles.html
     * http://mathworld.wolfram.com/InnerSoddyCircle.html
     * http://mathworld.wolfram.com/OuterSoddyCircle.html
     * @author YOSHIDA, Akio
     */
    /*public*/ class Calculator {
        /**
         * 3つの正接円の中心座標からそれぞれの半径を算出
         * @param    circleA
         * @param    circleB
         * @param    circleC
         */
        static public function calcTangentCircles(circleA:Circle, circleB:Circle, circleC:Circle):void {
            // 2つの正接円間の距離(2つの正接円の半径の和)を算出
            var distAB:Number = dist(circleA, circleB);    // = circleA.radius + circleB.radius
            var distBC:Number = dist(circleB, circleC);    // = circleB.radius + circleC.radius
            var distCA:Number = dist(circleC, circleA);    // = circleC.radius + circleA.radius
            // 各正接円の半径を算出
            circleA.radius = (distCA + distAB - distBC) / 2;
            circleB.radius = (distAB + distBC - distCA) / 2;
            circleC.radius = (distBC + distCA - distAB) / 2;
        }
        // 2点間の距離を求める
        static private function dist(circle1:Circle, circle2:Circle):Number {
            var distX:Number = circle1.x - circle2.x;
            var distY:Number = circle1.y - circle2.y;
            return Math.sqrt(distX * distX + distY * distY);
        }

        /**
         * 3つの正接円から、その内接円と外接円の半径と中心座標を算出
         * @param    circleA
         * @param    circleB
         * @param    circleC
         * @param    circleD
         * @param    circleE
         */
        static public function calcSoddyCircles(circleA:Circle, circleB:Circle, circleC:Circle, circleD:Circle, circleE:Circle):void {
            // ----- 4番目の円の半径を求める -----
            // 4番目の円の曲率k(半径の逆数 = 1/r)を求める公式
            // (k1 + k2 + k3 + k4)^2 = 2 * (k1^2 + k2^2 + k3^2 + k4^2)
            // ↓
            // k4 = k1 + k2 + k3 ± 2 * √(k1*k2 + k2*k3 + k3*k1)

            // 各正接円の曲率
            var kA:Number = 1 / circleA.radius;        // k1
            var kB:Number = 1 / circleB.radius;        // k2
            var kC:Number = 1 / circleC.radius;        // k3
            // ±前の部分
            var kABC:Number = kA + kB + kC;            // k1 + k2 + k3
            // ±後の部分
            var kAB:Number = kA * kB;                // k1 * k2
            var kBC:Number = kB * kC;                // k2 * k3
            var kCA:Number = kC * kA;                // k3 * k1
            var sq1:Number = 2 * Math.sqrt(kAB + kBC + kCA);    // 2 * √(k1*k2 + k2*k3 + k3*k1)
            // k4 の計算結果(±なのでふたつ)
            var radiusD:Number = 1 / (kABC - sq1);    // 外接円の半径(-の方)
            var radiusE:Number = 1 / (kABC + sq1);    // 内接円の半径(+の方)

            // ----- 4番目の円の中心座標を求める -----
            // 4番目の円の中心座標(複素平面上)を求める公式
            // (z1*k1 + z2*k2 + z3*k3 + z4*k4)^2 = 2 * (z1^2*k1^2 + z2^2*k2^2 + z3^2*k3^2 + z4^2*k4^2)
            // ↓
            // z4 = (z1*k1 + z2*k2 + z3*k3 ± 2 * √(z1*k1 * z2*k2 + z2*k2 * z3*k3 + z3*k3 * z1*k1)) / k4

            // 各正接円の座標
            var zA:Complex = new Complex(circleA.x, circleA.y);    // z1
            var zB:Complex = new Complex(circleB.x, circleB.y);    // z2
            var zC:Complex = new Complex(circleC.x, circleC.y);    // z3
            // 分子のうち、±前の部分
            var zkA:Complex   = ComplexMath.scale(zA, kA);        // z1 * k1
            var zkB:Complex   = ComplexMath.scale(zB, kB);        // z2 * k2
            var zkC:Complex   = ComplexMath.scale(zC, kC);        // z3 * k3
            var zkABC:Complex = ComplexMath.add(ComplexMath.add(zkA, zkB), zkC);
                                                                // z1*k1 + z2*k2 + z3*k3
            // 分子のうち、±後の部分
            var zkAB:Complex = ComplexMath.multiply(zkA, zkB);    // z1*k1 * z2*k2
            var zkBC:Complex = ComplexMath.multiply(zkB, zkC);    // z2*k2 * z3*k3
            var zkCA:Complex = ComplexMath.multiply(zkC, zkA);    // z3*k3 * z1*k1
            var zkAABBCC:Complex = ComplexMath.add(ComplexMath.add(zkAB, zkBC), zkCA);
                                                                // z1*k1 * z2*k2 + z2*k2 * z3*k3 + z3*k3 * z1*k1
            var sq2:Complex = ComplexMath.scale(ComplexMath.pow(zkAABBCC, 1 / 2), 2);
                                                                // 2 * √(z1*k1 * z2*k2 + z2*k2 * z3*k3 + z3*k3 * z1*k1)
            // z4 の計算結果(±なのでふたつ)
            var zD:Complex = ComplexMath.scale(ComplexMath.subtract(zkABC, sq2), radiusD);    // 外接円(-の方)
            var zE:Complex = ComplexMath.scale(ComplexMath.add(zkABC, sq2), radiusE);        // 内接円(+の方)

            // 外接円
            circleD.x = zD.real;
            circleD.y = zD.imag;
            circleD.radius = radiusD;
            // 内接円
            circleE.x = zE.real;
            circleE.y = zE.imag;
            circleE.radius = radiusE;
        }
    }
//}


//package aquioux.math {
    /**
     * 複素数
     * @author YOSHIDA, Akio
     */
    /*public*/ final class Complex {
        /**
         * 実数部
         */
        public function get real():Number { return _real; }
        public function set real(value:Number):void { _real = value; }
        public function get x():Number { return _real; }
        public function set x(value:Number):void { _real = value; }
        // 実数部
        private var _real:Number;

        /**
         * 虚数部
         */
        public function get imag():Number { return _imag; }
        public function set imag(value:Number):void { _imag = value; }
        public function get y():Number { return _imag; }
        public function set y(value:Number):void { _imag = value; }
        // 虚数部
        private var _imag:Number;


        /**
         * コンストラクタ
         * @param    real    実数部
         * @param    imag    虚数部
         */
        public function Complex(real:Number = 0.0, imag:Number = 0.0) {
            _real = real;
            _imag = imag;
        }

        /**
         * 複製
         * @return    複製した複素数
         */
        public function clone():Complex {
            return new Complex(_real, _imag);
        }

        /**
         * toString
         * @return    文字列表示
         */
        public function toString():String {
            return String(_real) + " + " + String(_imag) + " i";
        }
    }
//}


//package aquioux.math {
    /**
     * 複素数の定数と演算
     * @author YOSHIDA, Akio
     */
    /*public*/ final class ComplexMath {
        // ----- 定数 -----
        /**
         * ゼロ
         */
        static public const  ZERO:Complex = new Complex(0.0, 0.0);
        /**
         * 純実数1
         */
        static public const  PURE_REAL:Complex = new Complex(1.0, 0.0);
        /**
         * 純虚数i
         */
        static public const  PURE_IMAGINARY:Complex = new Complex(0.0, 1.0);


        // ----- 四則演算 -----
        /**
         * 加算
         * @param    a    加算項1
         * @param    b    加算項2
         * @return    加算結果
         */
        static public function add(a:Complex, b:Complex):Complex {
            return new Complex(
                a.real + b.real,
                a.imag + b.imag
            );
        }
        /**
         * 減算
         * @param    a    減算項1
         * @param    b    減算項2
         * @return    減算結果
         */
        static public function subtract(a:Complex, b:Complex):Complex {
            return new Complex(
                a.real - b.real,
                a.imag - b.imag
            );
        }
        /**
         * 乗算
         * @param    a    乗算項1
         * @param    b    乗算項2
         * @return    乗算結果
         */
        static public function multiply(a:Complex, b:Complex):Complex {
            var aRl:Number = a.real;
            var aIm:Number = a.imag;
            var bRl:Number = b.real;
            var bIm:Number = b.imag;
            return new Complex(
                aRl * bRl - aIm * bIm,
                aRl * bIm + aIm * bRl
            );
        }
        /**
         * 除算
         * @param    a    除算項1
         * @param    b    除算項2
         * @return    除算結果
         */
        static public function divide(a:Complex, b:Complex):Complex {
            return multiply(a, reciprocal(b));
        }

        /**
         * スケーリング(第2引数が実数である乗算)
         * @param    c    複素数
         * @param    n    実数
         * @return    乗算結果
         */
        static public function scale(c:Complex, n:Number):Complex {
            return new Complex(
                c.real * n,
                c.imag * n
            );
        }


        /**
         * 絶対値の2乗
         * @param    c    複素数
         * @return    絶対値の2乗
         */
        // c * c~ = c.real^2 + c.imag^2
        static public function absSquare(c:Complex):Number {
            var rl:Number = c.real;
            var im:Number = c.imag;
            return rl * rl + im * im;
        }
        /**
         * 絶対値(=長さ)
         * @param    c    複素数
         * @return    絶対値
         */
        // |c| = √(c * c~) = √(c.real^2 + c.imag^2)
        static public function abs(c:Complex):Number {
            return Math.sqrt(absSquare(c));
        }


        /**
         * 共役複素数
         * @param    c    複素数
         * @return    共役複素数
         */
        static public function conjugate(c:Complex):Complex {
            return new Complex(
                 c.real,
                -c.imag
            );
        }
        /**
         * 逆数
         * @param    c    複素数
         * @return    逆数
         */
        static public function reciprocal(c:Complex):Complex {
            var val:Number = absSquare(c);
            return new Complex(
                 c.real / val,
                -c.imag / val
            );
        }

        /**
         * 冪乗
         * @param    c    複素数
         * @param    exponent    冪指数
         * @return    冪乗結果
         */
        static public function pow(c:Complex, exponent:Number):Complex {
            // 極座標形式で計算
            var len:Number   = Math.pow(abs(c), exponent);
            var angle:Number = Math.atan2(c.imag, c.real) * exponent;
            return new Complex(
                len * Math.cos(angle),
                len * Math.sin(angle)
            );
        }


        /**
         * 直交座標形式から極座標形式へ
         * @param    c    直交座標形式の複素数
         * @return    極座標形式の複素数
         */
        // 対数関数との関連性
        static public function cartesianToPolar(c:Complex):Complex {
            return new Complex(
                abs(c),                        // 長さ
                Math.atan2(c.imag, c.real)    // 偏角
            );
        }
        /**
         * 極座標形式から直交座標形式へ
         * @param    c    極座標形式の複素数
         * @return    直交座標形式の複素数
         */
        // 指数関数との関連性
        static public function polarToCartesian(c:Complex):Complex {
            var len:Number   = c.real;
            var angle:Number = c.imag;
            return new Complex(
                len * Math.cos(angle),
                len * Math.sin(angle)
            );
        }


        /**
         * 対数関数
         * @param    c    複素数
         * @return    対数値
         */
        // 実数部は絶対値、虚数部は偏角
        static public function log(c:Complex):Complex {
            return new Complex(
                Math.log(abs(c)),
                Math.atan2(c.imag, c.real)
            );
        }
        /**
         * 指数関数
         * @param    c    複素数
         * @return    指数値
         */
        // e^x * (cosy + isiny)
        static public function exp(c:Complex):Complex {
            var val:Number = Math.exp(c.real);
            var im:Number  = c.imag;
            return new Complex(
                val * Math.cos(im),
                val * Math.sin(im)
            );
        }
    }
//}


//package {
    import flash.display.Graphics;
    import flash.display.Sprite;
    /**
     * 円クラス
     * @author YOSHIDA, Akio
     */
    /*public*/ class Circle extends Sprite {
        /**
         * 半径
         */
        public function get radius():Number { return _radius; }
        public function set radius(value:Number):void { _radius = value; }
        private var _radius:Number;

        /**
         * 描画色
         */
        public function set color(value:uint):void { _color = value; }
        private var _color:uint = 0x000000;


        /**
         * コンストラクタ
         */
        public function Circle(isDrag:Boolean = false):void {
            if (isDrag) this.buttonMode = true;
        }

        /**
         * 描画
         */
        public function draw():void {
            var g:Graphics = this.graphics;
            g.clear();
            g.beginFill(_color);
            g.drawCircle(0, 0, _radius);
            g.endFill();
        }
    }
//}


//package {
    import flash.display.Graphics;
    import flash.display.Shape;
    /**
     * 線クラス
     * @author YOSHIDA, Akio
     */
    /*public*/ class Line extends Shape {
        /**
         * 描画色
         */
        public function set color(value:uint):void { _color = value; }
        private var _color:uint = 0xffffff;


        /**
         * コンストラクタ
         */
        public function Line():void {
        }

        /**
         * 描画
         */
        public function draw(circleA:Circle, circleB:Circle, circleC:Circle):void {
            // 正接円の中心座標
            var xA:Number = circleA.x;
            var yA:Number = circleA.y;
            var xB:Number = circleB.x;
            var yB:Number = circleB.y;
            var xC:Number = circleC.x;
            var yC:Number = circleC.y;
            // 正接円の中心を結ぶ線の描画
            var g:Graphics = this.graphics;
            g.clear();
            g.lineStyle(0, _color);
            g.moveTo(xA, yA);
            g.lineTo(xB, yB);
            g.lineTo(xC, yC);
            g.lineTo(xA, yA);
        }
    }
//}