3つの円に直交する円の作図
3つの円に直交する円を作図します。
赤い点はドラッグ移動できます。
3円の中心の位置が一直線に近づくと、途中の計算が発散気味になります。
出展 数学100の勝利 31 "モンジュの問題"
/**
* Copyright keno42 ( http://wonderfl.net/user/keno42 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/2m7y
*/
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));
}
}