package { import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; /** * ... * @author 9re */ [SWF(backgroundColor = "#000000", frameRate = "37")] public class DocumentClass extends MovieClip { private var _cvt0:ConvexTetragon; private var _cvt1:ConvexTetragon; private var _ctDomain:ConvexTetragon; private var _ctRegion:ConvexTetragon; private var _pm:ProjectionMatrix; private var _pointer:Vertex; public function DocumentClass() { _cvt0 = new ConvexTetragon(); _cvt0.fillAlpha = 0.3; _cvt0.addPoint(new Point(30, 40)); _cvt0.addPoint(new Point(160, 60)); _cvt0.addPoint(new Point(200, 240)); _cvt0.addPoint(new Point(30, 160)); _cvt0.draw(); _cvt0.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler); _cvt0.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler); _cvt0.addEventListener(ConvexTetragon.VERTEX_MOVED, calculateProjectionMatirx); addChild(_cvt0); Vertex.COLOR = 0x0000ff; _cvt1 = new ConvexTetragon(); _cvt1.fillAlpha = 0.3; _cvt1.addPoint(new Point(300, 100)); _cvt1.addPoint(new Point(400, 140)); _cvt1.addPoint(new Point(400, 400)); _cvt1.addPoint(new Point(150, 350)); _cvt1.draw(); _cvt1.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler); _cvt1.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler); _cvt1.addEventListener(ConvexTetragon.VERTEX_MOVED, calculateProjectionMatirx); addChild(_cvt1); Vertex.COLOR = 0x00ff00; addChild(_pointer = new Vertex(new Point(0, 0))); _pointer.visible = false; _ctDomain = _cvt0; _ctRegion = _cvt1; _pm = new ProjectionMatrix(); calculateProjectionMatirx(null); } private function calculateProjectionMatirx(e:Event):void { var p:Point; setProjectionDomain(_ctDomain); setProjectionRegion(_ctRegion); _pm.calculateProjectionMatrix(); } private function setProjectionDomain($domain:ConvexTetragon):void { var p:Point; p = $domain.getPointAt(0); _pm.setDomainA(p.x, p.y); p = $domain.getPointAt(1); _pm.setDomainB(p.x, p.y); p = $domain.getPointAt(2); _pm.setDomainC(p.x, p.y); p = $domain.getPointAt(3); _pm.setDomainD(p.x, p.y); } private function setProjectionRegion($region:ConvexTetragon):void { var p:Point; p = $region.getPointAt(0); _pm.setRegionA(p.x, p.y); p = $region.getPointAt(1); _pm.setRegionB(p.x, p.y); p = $region.getPointAt(2); _pm.setRegionC(p.x, p.y); p = $region.getPointAt(3); _pm.setRegionD(p.x, p.y); } private function mouseOverHandler(e:MouseEvent):void { var cvt:ConvexTetragon = e.target as ConvexTetragon; if (cvt == _cvt0) { _ctDomain = _cvt0; _ctRegion = _cvt1; } else { _ctDomain = _cvt1; _ctRegion = _cvt0; } calculateProjectionMatirx(null); addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); _pointer.visible = true; } private function mouseMoveHandler(e:MouseEvent):void { _pointer.coordinate = _pm.convert(new Point(mouseX, mouseY)); } private function mouseOutHandler(e:MouseEvent):void { removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); _pointer.visible = false; } } } import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import gs.easing.*; import gs.TweenLite; class MMatrix { private var _column:int; private var _row:int; private var _arr:Array; public function MMatrix($row:int, $column:int) { _row = $row; _column = $column; _arr = []; } public function get column():int { return _column; } public function get row():int { return _row; } public function getElementAt($row:int, $column:int):Number { return _arr[$row * _column + $column]; } public function setElementAt($row:int, $column:int, $value:Number):void { _arr[$row * _column + $column] = $value; } } class MMatrixUtil { public static function makeMatrixFromArray($array:Array, $row:int, $column:int):MMatrix { var mat:MMatrix = new MMatrix($row, $column); var i:int, j:int; for (i = 0; i < $row; ++i) { for (j = 0; j < $column; ++j) { mat.setElementAt(i, j, $array[i * $column + j]); } } return mat; } } class HomogeneousLinearEQSystem { private var _dimension:int; private var _mat:MMatrix; private var _colInfo:Array; public function HomogeneousLinearEQSystem($array:Array):void { var n:int = Math.floor(Math.sqrt($array.length)); _dimension = n; _mat = MMatrixUtil.makeMatrixFromArray($array, n, n + 1); } public function solve():void { var i:int, j:int, k:int, ii:int, ik:int; var t:Number, u:Number, v:Number; var weight:Array; var colInfo:Array = []; var rowInfo:Array = []; weight = []; for (k = 0; k <= _dimension; ++k) colInfo[k] = k; for (k = 0; k < _dimension; ++k) { rowInfo[k] = k; u = 0; for (j = 0; j <= _dimension; ++j) { t = _mat.getElementAt(k, j); t = (t < 0) ? -t : t; u = (u < t) ? t : u; } weight[k] = 1 / u; // suppose u != 0 } for (k = 0; k < _dimension; ++k) { u = Number.NEGATIVE_INFINITY; for (i = k; i < _dimension; ++i) { ii = rowInfo[i]; t = _mat.getElementAt(ii, k) * weight[ii]; t = (t < 0) ? -t : t; if (t > u) { u = t; j = i; } } ik = rowInfo[j]; if (j != k) { rowInfo[j] = rowInfo[k]; rowInfo[k] = ik; } u = _mat.getElementAt(ik, k); if (u == 0) { //trace("u =", u); u = -1; for (j = k + 1; j <= _dimension; ++j) { t = _mat.getElementAt(ik, j); t = (t < 0) ? - t : t; if (u < t) { u = t; i = j; } } if (u == 0) { //trace("solved!"); //trace(rowInfo); _mat = exchangeRows(_mat, rowInfo); _colInfo = colInfo; return; } //trace("exchange columns:", k, i); //trace("(ik, i) =", _mat.getElementAt(ik, i)); // exchange columns j = colInfo[k]; colInfo[k] = colInfo[i]; colInfo[i] = j; // for (j = 0; j < _dimension; ++j) { t = _mat.getElementAt(j, k); _mat.setElementAt(j, k, _mat.getElementAt(j, i)); _mat.setElementAt(j, i, t); } u = _mat.getElementAt(ik, k); //trace("after: (ik, k) =", u); } ik = rowInfo[k]; for (j = k; j <= _dimension; ++j) { t = _mat.getElementAt(ik, j) / u; _mat.setElementAt(ik, j, t); } for (i = 0; i < _dimension; ++i) { if (i != k) { ii = rowInfo[i]; u = _mat.getElementAt(ii, k); for (j = k; j <= _dimension; ++j) { t = _mat.getElementAt(ii, j) - _mat.getElementAt(ik, j) * u; _mat.setElementAt(ii, j, t); } } } //trace(k, exchangeRows(_mat, rowInfo)); } //trace("solved!"); _mat = exchangeRows(_mat, rowInfo); _colInfo = colInfo; } public function get matrix():MMatrix { return _mat; } public function get columnInfo():Array { return _colInfo; } private function exchangeRows($matrix:MMatrix, $rowInfo:Array):MMatrix { var copy:MMatrix = new MMatrix(_dimension, _dimension + 1); var i:int, j:int, ii:int; for (i = 0; i < _dimension; ++i) { ii = $rowInfo[i]; for (j = 0; j < _dimension + 1; ++j) { copy.setElementAt(i, j, $matrix.getElementAt(ii, j)); } } return copy; } } class ProjectionMatrix { private var _dx0:Number; private var _dy0:Number; private var _dx1:Number; private var _dy1:Number; private var _dx2:Number; private var _dy2:Number; private var _dx3:Number; private var _dy3:Number; private var _rx0:Number; private var _ry0:Number; private var _rx1:Number; private var _ry1:Number; private var _rx2:Number; private var _ry2:Number; private var _rx3:Number; private var _ry3:Number; private var _coefficients:Array; private var _a0:Number; private var _b0:Number; private var _c0:Number; private var _a1:Number; private var _b1:Number; private var _c1:Number; private var _a2:Number; private var _b2:Number; private var _c2:Number; public function ProjectionMatrix() { } public function setDomainA($x:Number, $y:Number):void { _dx0 = $x; _dy0 = $y; } public function setDomainB($x:Number, $y:Number):void { _dx1 = $x; _dy1 = $y; } public function setDomainC($x:Number, $y:Number):void { _dx2 = $x; _dy2 = $y; } public function setDomainD($x:Number, $y:Number):void { _dx3 = $x; _dy3 = $y; } public function setRegionA($x:Number, $y:Number):void { _rx0 = $x; _ry0 = $y; } public function setRegionB($x:Number, $y:Number):void { _rx1 = $x; _ry1 = $y; } public function setRegionC($x:Number, $y:Number):void { _rx2 = $x; _ry2 = $y; } public function setRegionD($x:Number, $y:Number):void { _rx3 = $x; _ry3 = $y; } public function calculateProjectionMatrix():void { var hleqs:Array = [ _dx0 * _rx0, _dy0 * _rx0, _rx0, -_dx0, -_dy0, -1, 0, 0, 0, _dx1 * _rx1, _dy1 * _rx1, _rx1, -_dx1, -_dy1, -1, 0, 0, 0, _dx2 * _rx2, _dy2 * _rx2, _rx2, -_dx2, -_dy2, -1, 0, 0, 0, _dx3 * _rx3, _dy3 * _rx3, _rx3, -_dx3, -_dy3, -1, 0, 0, 0, _dx0 * _ry0, _dy0 * _ry0, _ry0, 0, 0, 0, -_dx0, -_dy0, -1, _dx1 * _ry1, _dy1 * _ry1, _ry1, 0, 0, 0, -_dx1, -_dy1, -1, _dx2 * _ry2, _dy2 * _ry2, _ry2, 0, 0, 0, -_dx2, -_dy2, -1, _dx3 * _ry3, _dy3 * _ry3, _ry3, 0, 0, 0, -_dx3, -_dy3, -1, ]; var solver:HomogeneousLinearEQSystem = new HomogeneousLinearEQSystem(hleqs); _coefficients = []; solver.solve(); var conInfo:Array = solver.columnInfo; var solution:MMatrix = solver.matrix; var ii:int; for (var i:int = 0; i < solution.row; ++i) { ii = conInfo[i]; _coefficients[ii] = - solution.getElementAt(i, solution.column - 1); } _coefficients[conInfo[solution.column - 1]] = 1; _a0 = _coefficients[0]; _b0 = _coefficients[1]; _c0 = _coefficients[2]; _a1 = _coefficients[3]; _b1 = _coefficients[4]; _c1 = _coefficients[5]; _a2 = _coefficients[6]; _b2 = _coefficients[7]; _c2 = _coefficients[8]; } public function convert($point:Point):Point { var x:Number = $point.x; var y:Number = $point.y; return new Point( (_a1 * x + _b1 * y + _c1) / (_a0 * x + _b0 * y + _c0), (_a2 * x + _b2 * y + _c2) / (_a0 * x + _b0 * y + _c0) ); } } class Vertex extends Sprite { public static var RADIUS:Number = 5; public static var COLOR:uint = 0xff0000; public function Vertex($point:Point) { x = $point.x; y = $point.y; graphics.beginFill(COLOR); graphics.drawCircle(0, 0, RADIUS); graphics.endFill(); } public function get coordinate():Point { return new Point(x, y); } public function set coordinate(value:Point):void { x = value.x; y = value.y; } } class Convex extends Sprite { private var _n:int; protected var _displayObjects:Array; public function Convex() { _displayObjects = []; } public function removeAllPoints():void { while (_displayObjects.length > 0) { removeChild(_displayObjects.pop()); } } public function getPointAt($index:int):Point { var v:Vertex = _displayObjects[$index] as Vertex; return v.coordinate; } public function addPoint($point:Point):void { _n = _displayObjects.push(addChild(new Vertex($point))); } public function get convexity():Boolean { if (_n < 4) return true; // _points.length > 3 var i:int, j:int; var sgn:int; var first:Point; var second:Point; var third:Point; var cross:Number; var v:Vertex; for (i = 0; i < _n; ++i) { v = _displayObjects[i] as Vertex; first = v.coordinate; sgn = 2; for (j = 1; j < _n - 1; ++j) { v = _displayObjects[(i + j) % _n]; second = v.coordinate; v = _displayObjects[(i + j + 1) % _n]; third = v.coordinate; second = second.subtract(first); third = third.subtract(first); cross = second.x * third.y - second.y * third.x; if (cross == 0) return false; if (sgn == 2) sgn = sign(cross); else if (sgn != sign(cross)) return false; } } return true; } private function sign(t:Number):int { return (t < 0) ? -1 : 1; } } class ConvexTetragon extends Convex { public static const VERTEX_MOVED:String = "vertex moved"; public var color:uint = 0xffffff; public var fillAlpha:Number = 1.0; private var _prevX:Number; private var _prevY:Number; private var _v:Vertex; public function ConvexTetragon() { //mouseEnabled = false; addEventListener(Event.ADDED_TO_STAGE, stageHandler, false, 0, true); } private function stageHandler(e:Event):void { stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } override public function addPoint($point:Point):void { super.addPoint($point); var v:Vertex = _displayObjects[_displayObjects.length - 1]; v.buttonMode = true; v.tabEnabled = false; v.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } private function mouseDownHandler(e:MouseEvent):void { _v = e.target as Vertex; _prevX = _v.x; _prevY = _v.y; stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); _v.startDrag(); } private function mouseUpHandler(e:MouseEvent):void { if (_v == null) return; _v.stopDrag(); stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); if (!convexity) { TweenLite.to(_v, 0.4, { x: _prevX, y:_prevY, ease:Cubic.easeOut, onComplete:removeEnterFrameHandler } ); addEventListener(Event.ENTER_FRAME, enterFrameHandler); } else { dispatchEvent(new Event(VERTEX_MOVED)); draw(); } draw(); _v = null; } private function removeEnterFrameHandler():void { removeEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function enterFrameHandler(e:Event):void { draw(); } public function draw():void { var v:Vertex; v = _displayObjects[0] as Vertex; graphics.clear(); graphics.beginFill(color, fillAlpha); graphics.moveTo(v.x, v.y); for (var i:int = 1; i <= 4; ++i) { v = _displayObjects[i & 3]; graphics.lineTo(v.x, v.y); } graphics.endFill(); } private function mouseMoveHandler(e:MouseEvent):void { e.updateAfterEvent(); draw(); } } Projection Matrix