fc2ブログ

線対称の位置を行列で求める

2008-03-14 | 21:10


原点を通ってx軸との角がΘで表される直線をはさんで対称の位置にある座標を求めるには行列
 
を掛ければよい。 (緑の線がx軸になるように軸を回転させ、yの値を反転させて、軸を元に戻す変換)

下の青いボールはマウスドラッグで移動できます。線の傾きは緑の点をドラッグして変えられます。赤いボールは青いボールと線対称の位置に自動的に移動します。

Mirror.swf

次に原点を通らない直線(Y = aX + b)の場合を考える。
x軸を下の図の点線の位置にずらして、直線が原点を通るような新しい座標系にしてしまえば上と同じ計算で求まる。

求まった座標は新しい座標系での座標なので、最後に元の座標系に戻せば答えが求まる。
x軸をbだけ上に移動する(3x3の)変換行列は

であり、 元の位置に移動させる(x軸をbだけ下げる)変換行列は

であるので 求めたい行列は

となる。

Mirror2.swf

Mirror.as
package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.utils.*;
    import flash.filters.DropShadowFilter;

    [SWF(width="500", height="500",backgroundColor="0xfcfcfc")]

        public class Mirror extends Sprite{
            private var balls:Array = [];
            private var blueBall:Ball = new Ball(0xff, 30);
            private var redBall:Ball = new Ball(0xff0000, 30);
            private var greenBall:Ball = new Ball(0xff00, 5);

            public function Mirror(){
                blueBall.x = 200;
                blueBall.y = 100;
                greenBall.x = greenBall.y = 300;
                addChild(blueBall);
                addChild(redBall);
                addChild(greenBall);

                initListener(blueBall);
                initListener(greenBall);

                cal();
            }

            private function initListener(b:Ball):void{
                b.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
                b.addEventListener( MouseEvent.MOUSE_UP, place );
                b.addEventListener( MouseEvent.MOUSE_MOVE, function(e:*):void{ cal(); });
            }

            public function pickup( event:MouseEvent ):void {
                // ドラッグ処理を開始して、影フィルターをつける
                event.target.startDrag( );
                event.target.filters = [ new DropShadowFilter( ) ];
                // 最前面に表示されるようにする
                setChildIndex( DisplayObject( event.target ), numChildren - 1 );
            }

            public function place( event:MouseEvent ):void {
                // ドラッグ処理を終了して影フィルターをオフ
                event.target.stopDrag( );
                event.target.filters = null;

                cal();
            }

            private function cal() :void{
                var theta:Number = Math.atan(greenBall.y/greenBall.x);

                redBall.x = blueBall.x * Math.cos(2*theta) + blueBall.y * Math.sin(2*theta);
                redBall.y = blueBall.x * Math.sin(2*theta) - blueBall.y * Math.cos(2*theta);

                graphics.clear();
                graphics.lineStyle(3, 0x0, 0.5);
                graphics.moveTo(0,0);
                graphics.lineTo(greenBall.x, greenBall.y);

            }
        }
}

import flash.display.*;

internal class Ball extends Sprite{
    public function Ball(color:int, radius:int){
        graphics.beginFill(color);
        graphics.drawCircle(0,0,radius);
        graphics.endFill();
    }
}

Mirror2.as
package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.utils.*;
    import flash.filters.DropShadowFilter;

    [SWF(width="500", height="500",backgroundColor="0xfcfcfc")]

        public class Mirror2 extends Sprite{
            private var balls:Array = [];
            private var blueBall:Ball = new Ball(0xff, 30);
            private var redBall:Ball = new Ball(0xff0000, 30);
            private var greenBall1:Ball = new Ball(0xff00, 5);
            private var greenBall2:Ball = new Ball(0xff00, 5);

            public function Mirror2(){
                blueBall.x = 200;
                blueBall.y = 100;
                greenBall1.x = greenBall1.y = 30;
                greenBall2.x = greenBall2.y = 300;
                addChild(blueBall);
                addChild(redBall);
                addChild(greenBall1);
                addChild(greenBall2);

                initListener(blueBall);
                initListener(greenBall1);
                initListener(greenBall2);

                cal();
            }

            private function initListener(b:Ball):void{
                b.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
                b.addEventListener( MouseEvent.MOUSE_UP, place );
                b.addEventListener( MouseEvent.MOUSE_MOVE, function(e:*):void{ cal(); });
            }

            public function pickup( event:MouseEvent ):void {
                // ドラッグ処理を開始して、影フィルターをつける
                event.target.startDrag( );
                event.target.filters = [ new DropShadowFilter( ) ];
                // 最前面に表示されるようにする
                setChildIndex( DisplayObject( event.target ), numChildren - 1 );
            }

            public function place( event:MouseEvent ):void {
                // ドラッグ処理を終了して影フィルターをオフ
                event.target.stopDrag( );
                event.target.filters = null;

                cal();
            }

            private function cal() :void{
                var a:Number = (greenBall2.y - greenBall1.y)/(greenBall2.x - greenBall1.x);
                var theta:Number = Math.atan(a);
                var b:Number = greenBall1.y - a*greenBall1.x;

                redBall.x = blueBall.x * Math.cos(2*theta) + blueBall.y * Math.sin(2*theta) - b*Math.sin(2*theta);
                redBall.y = blueBall.x * Math.sin(2*theta) - blueBall.y * Math.cos(2*theta) + b*Math.cos(2*theta) + b;

                graphics.clear();
                graphics.lineStyle(3, 0x0, 0.5);
                graphics.moveTo(greenBall1.x, greenBall1.y);
                graphics.lineTo(greenBall2.x, greenBall2.y);

            }
        }
}

import flash.display.*;

internal class Ball extends Sprite{
    public function Ball(color:int, radius:int){
        graphics.beginFill(color);
        graphics.drawCircle(0,0,radius);
        graphics.endFill();
    }
}

スポンサーサイト