package { import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; /** * 3つの円に直交する円を作図します。 * 赤い点はドラッグ移動できます。 * 3円の中心の位置が一直線に近づくと、途中の計算が発散気味になります。 * 出展 数学100の勝利 31 "モンジュの問題" */ public class Monju extends Sprite { private var mcA:MovableCircle = new MovableCircle(60, 0xFF00FFFF, 3, 0xFFFF0000, true); private var mcB:MovableCircle = new MovableCircle(80, 0xFF00FF00, 3, 0xFFFF0000, true); private var mcC:MovableCircle = new MovableCircle(90, 0xFF0000FF, 3, 0xFFFF0000, true); public function Monju() { mcA.x = 90; mcA.y = 120; addChild(mcA); mcB.x = 340; mcB.y = 180; addChild(mcB); mcC.x = 140; mcC.y = 350; addChild(mcC); this.addEventListener("startRefresh", onStartRefresh, true); this.addEventListener("stopRefresh", onStopRefresh, true); _refresh(); } private function onStopRefresh(e:Event):void { this.removeEventListener(Event.ENTER_FRAME, onEnterFrame); _refresh(); } private function onStartRefresh(e:Event):void { this.addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(e:Event):void { _refresh(); } private function _refresh():void { var f:Number, l:Number; // 根線(A,B) l = Math.sqrt(Math.pow(mcB.x - mcA.x, 2) + Math.pow(mcB.y - mcA.y, 2)); f = (Math.pow(l, 2) - Math.pow(mcB.radius, 2) + Math.pow(mcA.radius, 2)) / (2 * l) var p1:Point = GeomUtil.getNaibunten(mcA.x, mcA.y, mcB.x, mcB.y, f/l); var p2:Point = new Point(p1.x + (mcB.y - mcA.y), p1.y - (mcB.x - mcA.x)); // 根線(A,C) l = Math.sqrt(Math.pow(mcC.x - mcA.x, 2) + Math.pow(mcC.y - mcA.y, 2)); f = (Math.pow(l, 2) - Math.pow(mcC.radius, 2) + Math.pow(mcA.radius, 2)) / (2 * l) var p3:Point = GeomUtil.getNaibunten(mcA.x, mcA.y, mcC.x, mcC.y, f/l); var p4:Point = new Point(p3.x + (mcC.y - mcA.y), p3.y - (mcC.x - mcA.x)); // 根点 var p5:Point = GeomUtil.getKoten(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); var radius:Number = Math.sqrt( (Math.pow(p5.x - mcA.x, 2) + Math.pow(p5.y - mcA.y, 2)) - Math.pow(mcA.radius,2) ); this.graphics.clear(); // 半径が大きいとき、なんか途中でオーバーフローとかしちゃってる? if ( radius > 4000 ) { this.graphics.lineStyle(0, 0xFF0000); } else { this.graphics.lineStyle(0, 0x88FFC8); } if( !isNaN(radius) ){ this.graphics.drawCircle(p5.x, p5.y, radius); } } } } import flash.display.*; import flash.events.*; import flash.geom.Matrix; // ドラッグで移動する点 class MovablePoint extends Sprite{ public function MovablePoint(radius:Number, color:uint, isMovable:Boolean){ this.graphics.lineStyle(1, color); this.graphics.beginFill(color & 0xFFFFFF, 0.75); this.graphics.drawCircle(0, 0, radius); if( isMovable ){ this.addEventListener(MouseEvent.MOUSE_DOWN, onDown); this.buttonMode = true; } else { this.mouseEnabled = this.mouseChildren = false; } } protected function onDown(e:MouseEvent):void{ this.startDrag(); this.dispatchEvent(new Event("startRefresh")); this.addEventListener(MouseEvent.MOUSE_UP, onUp); this.stage.addEventListener(MouseEvent.MOUSE_UP, onUp); } private function onUp(e:MouseEvent):void{ this.stopDrag(); this.dispatchEvent(new Event("stopRefresh")); this.removeEventListener(MouseEvent.MOUSE_UP, onUp); this.stage.removeEventListener(MouseEvent.MOUSE_UP, onUp); } } // 円 class MovableCircle extends MovablePoint { public var radius:Number; private var _mp:MovablePoint; private var _line:Sprite = new Sprite; private var _circleColor:uint; public function MovableCircle(radius:Number, circleColor:uint, pointRadius:Number, pointColor:uint, isMovable:Boolean) { _circleColor = circleColor; _mp = new MovablePoint(pointRadius, pointColor, isMovable); _mp.alpha = 0.75; var theta:Number = Math.random() * Math.PI * 2; _mp.x = radius * Math.cos(theta); _mp.y = radius * Math.sin(theta); this.radius = radius; if( isMovable ){ this.addEventListener("startRefresh", onStartRefresh, true); this.addEventListener("stopRefresh", onStopRefresh, true); } super(pointRadius, pointColor, isMovable); addChild(_line); addChild(_mp); _refresh(); } private function onStopRefresh(e:Event):void { _isChildDragging = false; this.removeEventListener(Event.ENTER_FRAME, onEnterFrame); _refresh(); } private function onStartRefresh(e:Event):void { _isChildDragging = true; this.addEventListener(Event.ENTER_FRAME, onEnterFrame); } private var _isChildDragging:Boolean = false; override protected function onDown(e:MouseEvent):void { if ( _isChildDragging ) { return; } super.onDown(e); } private function onEnterFrame(e:Event):void { _refresh(); } private function _refresh():void { radius = Math.sqrt(Math.pow(_mp.x, 2) + Math.pow(_mp.y, 2)); _line.graphics.clear(); _line.graphics.lineStyle(0, _circleColor & 0xFFFFFF); _line.graphics.lineTo(_mp.x, _mp.y); _line.graphics.drawCircle(0, 0, radius); } } import flash.geom.Point; class GeomUtil { // 垂点座標計算メソッド public static function getSuiten(Ax:Number, Ay:Number, Bx:Number, By:Number, Cx:Number, Cy:Number):Point{ var A:Point = new Point(Ax, Ay); // 直線上の点A var B:Point = new Point(Bx, By); // 直線上の点B var C:Point = new Point(Cx, Cy); // 直線外の点C var AB:Point = B.subtract(A); // ベクトルAB var unitAB:Point = AB.clone(); // ABの単位ベクトル unitAB.normalize(1); var AC:Point = C.subtract(A); // ベクトルAC // ACのAB方向の成分を取得 var unitABxAC:Number = unitAB.x*AC.x+unitAB.y*AC.y; // ACのAB軸への射影を計算 var ret:Point = new Point(unitAB.x*unitABxAC, unitAB.y*unitABxAC); return ret.add(A); // A+上の射影が垂点の位置 } // 直線ABと直線CDの交点座標計算メソッド public static function getKoten(Ax:Number, Ay:Number, Bx:Number, By:Number, Cx:Number, Cy:Number, Dx:Number, Dy:Number):Point { var mat:Matrix = new Matrix(By - Ay, Dy - Cy, Ax - Bx, Cx - Dx); var XY:Point = mat.transformPoint(new Point(Cx - Ax, Cy - Ay)); var abcd:Number = mat.a * mat.d - mat.b * mat.c; XY.x /= abcd; return new Point(Cx + XY.x * (Dx - Cx), Cy + XY.x * (Dy - Cy)); } // 線分ABの中点座標計算メソッド public static function getChuten(Ax:Number, Ay:Number, Bx:Number, By:Number):Point { return new Point(Ax + 0.5 * (Bx - Ax), Ay + 0.5 * (By - Ay)); } // 線分ABをs:1-sに内分する点の座標計算メソッド public static function getNaibunten(Ax:Number, Ay:Number, Bx:Number, By:Number, s:Number):Point { return new Point(Ax + s * (Bx - Ax), Ay + s * (By - Ay)); } } 3つの円に直交する円の作図