Matrix Tutorial
♥12 |
Line 345 |
Modified 2009-10-28 14:06:15 |
MIT License
archived:2017-03-07 06:48:33
| (replaced)
Related images
ActionScript3 source code
/**
* Copyright 9re ( http://wonderfl.net/user/9re )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/xO2m
*/
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0"
creationComplete="init();" frameRate="30">
<mx:Script><![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.TriangleCulling;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import mx.collections.ArrayCollection;
import mx.controls.TextInput;
import mx.core.UIComponent;
public var fileRef:FileReference;
private const DEFAULT_IMAGE:String = "http://assets.wonderfl.net/images/related_images/d/d0/d028/d028067724d74b7aba05141bc958aae5c16ffb08";
private var _bmd:BitmapData;
private var _ldr:Loader = new Loader;
private var _clearCTFM:ColorTransform = new ColorTransform(1, 1, 1, 0);
private var _init:Boolean;
private var _lock:Boolean = false;
private var _currentMatrix:Matrix = new Matrix;
private var _animationData:Vector.<Matrix>;
private function init():void {
_animationData = new Vector.<Matrix>;
makeSampleData();
clearTexts();
_init = true;
_bmd = new BitmapData(300, 465);
var sp:Sprite = new Sprite;
sp.addChild(new Bitmap(_bmd));
var ui:UIComponent = new UIComponent;
ui.addChild(sp);
canvas.addChild(ui);
_ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
// set checkPolicyFile true when you use images placed
// under assts.wonderfl.net
_ldr.load(new URLRequest(DEFAULT_IMAGE), new LoaderContext(true));
}
private function makeSampleData():void
{
var m:Matrix = new Matrix;
m = new Matrix;
m.translate( -50, -50);
tfTranslateX.text = "-50";
tfTranslateY.text = "-50";
generateAnimation("translate", -50, -50);
appendMatrix(m);
tfScaleX.text = "2";
tfScaleY.text = "2";
generateAnimation("scale", 2, 2);
m = new Matrix;
m.scale(2, 2);
appendMatrix(m);
tfRotation.text = "Math.PI*4/3";
generateAnimation("rotate", Math.PI * 4 / 3);
m = new Matrix;
m.rotate(Math.PI * 4 / 3);
appendMatrix(m);
tfTranslateX.text = "100";
tfTranslateY.text = "100";
generateAnimation("translate", 100, 100);
m = new Matrix;
m.translate(100, 100);
appendMatrix(m);
}
private function onImageLoaded($event:Event):void
{
_ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, onImageLoaded);
draw();
}
private function draw():void
{
if (!_init) return;
script.text = "var mat:Matrix = new Matrix();\n";
var item:Object;
for (var i:int = 0; i < dpMatrixData.length; ++i) {
item = dpMatrixData.getItemAt(i);
script.text += "mat." + item.transform + ";\n";
}
_bmd.colorTransform(_bmd.rect, _clearCTFM);
_bmd.draw(_ldr, _currentMatrix);
script.text += "bitmapData.draw(loader, mat);";
}
private function changeImage():void {
fileRef = new FileReference;
fileRef.browse([new FileFilter("Images(*.jpg, *.jpeg, *.png)", "*.jpg;*.jpeg;*.png;")]);
fileRef.addEventListener(Event.SELECT, fileSelected);
}
private function fileSelected($event:Event):void
{
fileRef.addEventListener(Event.COMPLETE, onFileLoaded);
fileRef.load();
}
private function onFileLoaded(e:Event):void
{
fileRef.removeEventListener(Event.COMPLETE, onFileLoaded);
fileRef.removeEventListener(Event.SELECT, fileSelected);
_ldr = new Loader;
_ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
_ldr.loadBytes(fileRef.data);
}
private function converToNumber($textInput:TextInput):Number {
var original:String = $textInput.text.replace(/ /g, "");
var n:Number = NaN;
try {
n = parseFloat(parse(original));
} catch(e:Error) {
}
if (isNaN(n)) {
$textInput.text = "0";
n = 0;
} else {
$textInput.text = original;
}
return n;
}
private function clearTexts():void {
tfRotation.text = "";
tfScaleX.text = "";
tfScaleY.text = "";
tfTranslateX.text = "";
tfTranslateY.text = "";
}
private function clearMatrix():void {
if (_lock) return;
_currentMatrix = new Matrix;
_animationData.length = 0;
clearTexts();
cbTransform.selectedIndex = 0;
dpMatrixData.removeAll();
}
public function appendMatrix($matrix:Matrix):void {
// skip if matrix is an eigen matix
if ($matrix.a == 1 && $matrix.b == 0 && $matrix.c == 0 && $matrix.d == 1 && $matrix.tx == 0 && $matrix.ty == 0)
return;
var item:Object = { };
if ($matrix.b != 0 || $matrix.c != 0) { // rotation matrix
item.transform = "rotate(" + tfRotation.text + ")";
} else if ($matrix.tx != 0 || $matrix.ty != 0) { // translation matrix
item.transform = "translate(" + tfTranslateX.text + ", " + tfTranslateY.text + ")";
} else { // scaling matrix
item.transform = "scale(" + tfScaleX.text + ", " + tfScaleY.text + ")";
}
_currentMatrix.concat($matrix);
dpMatrixData.addItem(item);
}
private function apply():void {
if (_lock) return;
_lock = true;
var a:Number, b:Number;
var m:Matrix = new Matrix;
switch (cbTransform.selectedIndex) {
case 0: // rotation
a = converToNumber(tfRotation);
m.rotate(a);
generateAnimation("rotate", a);
break;
case 1: // scale
a = converToNumber(tfScaleX);
b = converToNumber(tfScaleY);
m.scale(a, b);
generateAnimation("scale", a, b);
break;
case 2: // translate
a = converToNumber(tfTranslateX);
b = converToNumber(tfTranslateY);
m.translate(a, b);
generateAnimation("translate", a, b);
break;
}
appendMatrix(m);
_lock = false;
}
private function generateAnimation($operation:String, $a:Number, $b:Number = 0):void {
var m:Matrix, c:Matrix;
var t:Number, u:Number;
for (var i:int = 0; i < 30; ++i) {
m = new Matrix;
c = _currentMatrix.clone();
t = (i + 1) / 30;
u = 1 - t;
switch($operation) {
case "rotate":
m.rotate(t * $a);
c.concat(m);
_animationData.push(c);
break;
case "scale":
m.scale(u + t * $a, u + t * $b);
c.concat(m);
_animationData.push(c);
break;
case "translate":
m.translate(u + t * $a, u + t * $b);
c.concat(m);
_animationData.push(c);
break;
}
}
}
private function startAnimation():void {
_lock = true;
trace("startAnimation", _animationData.length);
addEventListener(Event.ENTER_FRAME, (
function():Function {
var i:int = 0;
return function ($event:Event):void {
if (i == _animationData.length) {
removeEventListener(Event.ENTER_FRAME, arguments.callee);
_lock = false;
} else {
if (i % 30 == 0)
dg.selectedIndex = int(Math.floor(i / 30));
_bmd.colorTransform(_bmd.rect, _clearCTFM);
_bmd.draw(_ldr, _animationData[i++]);
}
}
}
)());
}
// Math.PIと小数の演算が出来るように改造
// http://d.hatena.ne.jp/nitoyon/20090128/as3_simple_parser
// 10分で書ける、お手軽パーザーを AS3 で
// Simple Recursive Descent Parsing
// see also: http://fxp.hp.infoseek.co.jp/arti/parser.html
private function parse($expr:String):String {
var pos:int = 0;
var str:String = $expr.replace(/ /g, "").replace(/Math.PI/g, Math.PI.toString());
// Expr = Term { (+|-) Term}
var expr:Function = function():Number{
var ret:Number = term();
while(true){
switch(str.charAt(pos)){
case "+": pos++; ret += term(); break;
case "-": pos++; ret -= term(); break;
default: return ret;
}
}
return 0; // never comes here
};
// Term = Fact { (*|/) Fact}
var term:Function = function():Number{
var ret:Number = fact();
while(true){
switch(str.charAt(pos)){
case "*": pos++; ret *= fact(); break;
case "/": pos++; ret /= fact(); break;
default: return ret;
}
}
return 0; // never comes here
};
// Fact = ( Expr ) | - Fact | number
var fact:Function = function():Number{
var ret:Number;
var m:Array;
if((m = str.substr(pos).match(/^(\d+(\.\d+)?)/))){
pos += m[1].length;
return parseFloat(m[1]);
}
else if(str.charAt(pos) == "-"){
pos++;
return -fact();
}
else if(str.charAt(pos) == "("){
pos++;
ret = expr();
if(str.charAt(pos) != ")") throw new Error("No match for )");
pos++;
return ret;
}
throw new Error("invalid format");
};
return expr().toString();
}
]]></mx:Script>
<mx:ArrayCollection id="dpMatrixData" collectionChange="draw();" />
<mx:ArrayCollection id="transforms">
<mx:Object label="rotate" angle="0" index="0" />
<mx:Object label="scale" scaleX="1" scaleY="1" index="1" />
<mx:Object label="translate" translateX="0" translateY="0" index="2" />
</mx:ArrayCollection>
<mx:HDividedBox width="100%" height="100%" resizeToContent="true">
<mx:VBox height="100%">
<mx:HBox>
<mx:Button label="clear matrix" click="clearMatrix();" />
<mx:Button label="change image" click="changeImage();" />
</mx:HBox>
<mx:Spacer height="5" />
<mx:TabNavigator width="100%" paddingTop="0" paddingBottom="0" creationPolicy="all">
<mx:VBox label="transforms" height="100%">
<mx:DataGrid id="dg" dataProvider="{dpMatrixData}" width="100%" height="100%" sortableColumns="false" />
</mx:VBox>
<mx:VBox label="view script" height="100%">
<mx:TextArea id="script" width="100%" height="100%" />
</mx:VBox>
</mx:TabNavigator>
<mx:VBox height="100%">
<mx:VBox>
<mx:Spacer height="5" />
<mx:HBox>
<mx:Label text="transform" />
<mx:ComboBox id="cbTransform" dataProvider="{transforms}" />
</mx:HBox>
<mx:Spacer height="5" />
<mx:HBox>
<mx:Label text="apply transform:" />
<mx:Label text="{cbTransform.selectedItem.label}" />
</mx:HBox>
<mx:ViewStack creationPolicy="all" resizeToContent="true" selectedIndex="{cbTransform.selectedIndex}">
<mx:HBox>
<mx:Label text="rotate" />
<mx:VBox>
<mx:TextInput id="tfRotation" />
<mx:Text text="e.g. Math.PI / 3" />
</mx:VBox>
</mx:HBox>
<mx:VBox>
<mx:HBox>
<mx:Label text="scaleX" />
<mx:TextInput id="tfScaleX" />
</mx:HBox>
<mx:HBox>
<mx:Label text="scaleY" />
<mx:TextInput id="tfScaleY" />
</mx:HBox>
</mx:VBox>
<mx:VBox>
<mx:HBox>
<mx:Label text="translateX" />
<mx:TextInput id="tfTranslateX" />
</mx:HBox>
<mx:HBox>
<mx:Label text="translateY" />
<mx:TextInput id="tfTranslateY" />
</mx:HBox>
</mx:VBox>
</mx:ViewStack>
<mx:Spacer height="5" />
<mx:HBox>
<mx:Button label="apply" click="apply();" />
<mx:Button label="show animation" click="startAnimation();" />
</mx:HBox>
</mx:VBox>
</mx:VBox>
</mx:VBox>
<mx:Canvas id="canvas" />
</mx:HDividedBox>
</mx:Application>