Projection Matrix
...
@author 9re
/**
* Copyright 9re ( http://wonderfl.net/user/9re )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/k8MW
*/
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();
}
}