├── .gitignore ├── LICENSE ├── README.md ├── akifox-transform.sublime-project ├── com └── akifox │ └── transform │ └── Transformation.hx ├── docs ├── bootstrap-combined.min.css ├── bootstrap.min.js ├── favicon.ico ├── index.html ├── index.js ├── jquery-1.9.1.min.js ├── nav.js ├── styles.css ├── triangle-closed.png └── triangle-opened.png ├── haxelib.json ├── samples └── interactive │ ├── README.md │ ├── assets │ ├── fonts │ │ └── 04B_03__.ttf │ ├── graphics │ │ ├── graphics_here │ │ └── test.png │ └── openfl.svg │ ├── project.xml │ └── src │ └── Main.hx └── scripts ├── gen_docs.hxml ├── gen_docs.sh └── make_haxelib.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *bin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Simone Cingano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![akifox-transform](https://img.shields.io/badge/library-akifox%20transform%202.2.1-brightgreen.svg)]() 2 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 3 | [![Haxe 3](https://img.shields.io/badge/language-Haxe%203-orange.svg)](http://www.haxe.org) 4 | [![OpenFL 2](https://img.shields.io/badge/require-OpenFL 2-red.svg)](http://www.openfl.org) 5 | [![Cross platform](https://img.shields.io/badge/platform-cross%20platform-lightgrey.svg)](http://www.openfl.org) 6 | 7 | [![Library](https://img.shields.io/badge/type-haxelib%20library-orange.svg)](http://lib.haxe.org/p/akifox-transform) 8 | [![Haxelib](https://img.shields.io/badge/distr-v2.2.1-yellow.svg)](http://lib.haxe.org/p/akifox-transform) 9 | 10 | # akifox-transform (com.akifox.transform.Transformation) 11 | **HAXE/OpenFL Affine transformation class** 12 | 13 | The akifox-transform class aims to provide an easy tool to manage affine transformations using a reliable pivot point. 14 | What are the affine transformation you might ask... 15 | - read this wikipedia page 16 | - read this great flash tutorial 17 | 18 | # NOTE 19 | 20 | The class works quite fine with OpenFL 3, there are just few incompatibilities that might present problems. 21 | If you see a possible bug please test your app with the option `-Dlegacy` before filing an issue. 22 | 23 | ## Example demo 24 | 25 | ![Screenshot](https://dl.dropboxusercontent.com/u/683344/akifox/akifox-transform/transformation-example.png) 26 | 27 | **Flash build:** transformation-example.swf 28 | 29 | You should get a window with a OpenFL logo square. 30 | - Z to toggle debug drawings 31 | - SPACE to reset the transformations 32 | - Drag to move 33 | - Click to change the Pivot Point 34 | - Drag+SHIFT to rotate around the pivot point 35 | - Drag+ALT to scale related to the pivot point 36 | - Drag+CMD/CTRL to skew related to the pivot point (the cross center represents a 0,0 skew) 37 | - 1 to 9 to set the pivot point on the relative anchor point (TOPLEFT, MIDDLELEFT,BOTTOMLEFT,TOPCENTER... BOTTOMRIGHT) 38 | - UP, DOWN, RIGHT, LEFT Move 15px 39 | - Q, A to Skew X ±15deg 40 | - W, S to Skew Y ±15deg 41 | - E, D to Scale */1.5 42 | - R, F to Rotate ±15deg 43 | 44 | 45 | ## Install 46 | 47 | You can easily install the library thru haxelib 48 | 49 | ``` 50 | haxelib install akifox-transform 51 | ``` 52 | 53 | In your project add the library reference in your ```project.xml``` 54 | 55 | ``` 56 | 57 | ``` 58 | 59 | and finally you can import it in your project class with this import 60 | ``` 61 | import com.akifox.transform.Transformation; 62 | ``` 63 | 64 | ## Documentation 65 | 66 | You can read the full Library documentation here 67 | 68 | 69 | 70 | ## Using the library 71 | 72 | The Transformation class works on Matrix objects. 73 | Anyway usually once you've got a DisplayObject (Sprites, Bitmap...) you want to link this to a Transformation. 74 | 75 | ````haxe 76 | package ; 77 | import openfl.display.Sprite; 78 | import openfl.Lib; 79 | import openfl.geom.Matrix; 80 | import com.akifox.transform.Transformation; 81 | 82 | class Main extends Sprite { 83 | 84 | public function new() { 85 | super(); 86 | Lib.current.stage.addChild(this); 87 | 88 | //[...] 89 | 90 | var trf = new Transformation(); 91 | trf.bind(yourDisplayObject); 92 | trf.setAnchoredPivot(Transformation.ANCHOR_TOP_LEFT); 93 | 94 | // these are the Pivot Point coordinates (they will not change unless 95 | // you change the pivot point position) 96 | var pivotCoordinates:Point = trf.getPivot(); 97 | 98 | trf.rotate(20); //rotate by 20deg clockwise 99 | trf.skewX(30); //skew X axis by 30deg 100 | Actuate.tween(trf,1,{'scalingX':2,'scalingY'"2}); //scale 2X in 1s using Actuate 101 | } 102 | 103 | } 104 | ```` 105 | 106 | But you can use the library to manipulate a Matrix without bind it to a DisplayObject 107 | 108 | ````haxe 109 | package ; 110 | import openfl.display.Sprite; 111 | import openfl.Lib; 112 | import openfl.geom.Matrix; 113 | import com.akifox.transform.Transformation; 114 | 115 | class Main extends Sprite { 116 | 117 | var trf:Transformation; 118 | 119 | public function new() { 120 | super(); 121 | Lib.current.stage.addChild(this); 122 | trf = new Transformation(new Matrix(),100,50); 123 | addChild(trf.spriteDebug); //debug 124 | trf.addEventListener(Transformation.TRANSFORM, onTransform); //debug 125 | trf.rotate(20); //rotate by 20deg clockwise 126 | trf.skewX(30); //skew X axis by 30deg 127 | 128 | var transformedMatrix = trf.matrix; //get the matrix for your own use! 129 | } 130 | 131 | public function onTransform(event:Event) { 132 | trf.debugDraw(); 133 | } 134 | 135 | } 136 | ```` 137 | 138 | ## Best practice 139 | 140 | The idea behind the library wants the developer to use the transformation to change the object affine transformation properties. 141 | 142 | So you can work on the large amount of transformation properties and methods as: 143 | 144 | These assignments modify the target/matrix property according to the pivot point 145 | (all of the degree ones are provided in Rad as well) 146 | ```` 147 | trf.x = valuePixels; 148 | trf.y = valuePixels; 149 | trf.rotation = valueDegrees; 150 | trf.skewingX = valueDegrees; 151 | trf.skewingY = valueDegrees; 152 | trf.scaling = valueFactor; //set X and Y scale to this factor 153 | trf.scalingX = valueFactor; 154 | trf.scalingY = valueFactor; 155 | ```` 156 | 157 | The methods provide instead a algebric sum change according to the pivot point 158 | (all of the degree ones are provided in Rad as well) 159 | ```` 160 | trf.translate(addPixelsX,addPixelsY); 161 | trf.translateX(addPixels); 162 | trf.translateY(addPixels); 163 | trf.rotate(addDegrees); 164 | trf.skewX(addDegree); 165 | trf.skewY(addDegree); 166 | trf.scale(multiplyFactor); 167 | trf.scaleX(multiplyFactor); 168 | trf.scaleY(multiplyFactor); 169 | ```` 170 | 171 | ----- 172 | 173 | There are some interesting examples in different classes on the [PLIK library](https://github.com/yupswing/plik) that shows how to encapsulate the transformation class with an object. 174 | See the [Gfx Class](https://github.com/yupswing/plik/blob/master/com/akifox/plik/Gfx.hx), or Text, or SpriteContainer for an example. 175 | 176 | #### Transformation class 177 | - [ ] *Unit test* 178 | - [x] Cleaning and documenting code 179 | - [x] Pivot point managing 180 | - [x] Support for motion.Actuate (properties linked to functions get and set) 181 | - [x] Events (Transform and Pivot change) 182 | - [x] Translate 183 | - [x] Get 184 | - [x] Set 185 | - [x] Add 186 | - [x] Skew 187 | - [x] Get 188 | - [x] Set 189 | - [x] Add 190 | - [x] Scale 191 | - [x] Get 192 | - [x] Set 193 | - [x] Add 194 | - [ ] Flip 195 | - [ ] Get (it looks like impossible!) 196 | - [x] Set 197 | - [x] Rotate 198 | - [x] Get 199 | - [x] Set 200 | - [x] Add 201 | -------------------------------------------------------------------------------- /akifox-transform.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": "." 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /com/akifox/transform/Transformation.hx: -------------------------------------------------------------------------------- 1 | package com.akifox.transform; 2 | 3 | import openfl.geom.Matrix; 4 | import openfl.geom.Point; 5 | import openfl.events.Event; 6 | import openfl.events.EventDispatcher; 7 | import openfl.display.Shape; 8 | import openfl.display.DisplayObject; 9 | 10 | /** 11 | 12 | @author Simone Cingano (yupswing) [Akifox Studio](http://akifox.com) 13 | 14 | @licence MIT Licence 15 | 16 | @version 2.2.1 17 | [Public repository](https://github.com/yupswing/akifox-transform/) 18 | 19 | #### Transformation HAXE/OpenFL Library 20 | The akifox-transform library aims to provide an easy tool 21 | to manage affine transformations using a reliable pivot point. 22 | 23 | #### Notes: 24 | This page was very helpful to understand matrix affine transformation 25 | http://www.senocular.com/flash/tutorials/transformmatrix/ 26 | 27 | */ 28 | class Transformation extends EventDispatcher 29 | { 30 | /** Constant factor to pass from degrees to radians **/ 31 | public static var DEG2RAD:Float = Math.PI/180; 32 | /** Constant factor to pass from radians to degrees **/ 33 | public static var RAD2DEG:Float = 180/Math.PI; 34 | // temporary objects (always reset them before using) 35 | private static var point:Point = new Point(); 36 | private static var point2:Point = new Point(); 37 | 38 | private inline static var NULL:Float=-0.01; 39 | 40 | // Pivot Point Anchors (used by setAnchoredPivot and getAnchoredPivot) 41 | public inline static var ANCHOR_TOP_LEFT:Int = 0; 42 | public inline static var ANCHOR_TOP_CENTER:Int = 1; 43 | public inline static var ANCHOR_TOP_RIGHT:Int = 2; 44 | public inline static var ANCHOR_MIDDLE_LEFT:Int = 3; 45 | public inline static var ANCHOR_MIDDLE_CENTER:Int = 4; 46 | public inline static var ANCHOR_MIDDLE_RIGHT:Int = 5; 47 | public inline static var ANCHOR_BOTTOM_LEFT:Int = 6; 48 | public inline static var ANCHOR_BOTTOM_CENTER:Int = 7; 49 | public inline static var ANCHOR_BOTTOM_RIGHT:Int = 8; 50 | 51 | // the pivot anchor point (using Pivot Point Anchors) 52 | // example: Transformation.ANCHOR_BOTTOM_CENTER 53 | private var pivotPointAnchor:Int = -1; 54 | 55 | // the pivot offset 56 | private var offsetPoint:Point; 57 | 58 | // the target object 59 | private var _target:DisplayObject = null; 60 | 61 | // target properties 62 | private var _width:Float; 63 | private var _height:Float; 64 | private var _identityX:Float; 65 | private var _identityY:Float; 66 | private var _identityWidth:Float; 67 | private var _identityHeight:Float; 68 | /** The debug sprite where debugDraw() draws **/ 69 | public var spriteDebug:Shape; 70 | 71 | // THE MATRIX! 72 | private var _matrix:Matrix; 73 | 74 | 75 | /** 76 | * Set the transformation matrix, overwriting the existing one 77 | * (the pivot point will be respected) 78 | * Get a copy of the transformation matrix 79 | **/ 80 | public var matrix(get,set):Matrix; 81 | private function set_matrix(m:Matrix):Matrix { 82 | // change the matrix and adjust to respect the Pivot Point 83 | setMatrixInternal(m,true); 84 | return m; 85 | } 86 | private function get_matrix():Matrix { 87 | return getMatrixInternal(); 88 | } 89 | 90 | /** 91 | * Class instance 92 | * 93 | * All the parameters are optional and you don't need to exclicit them if you plan 94 | * to bind the transformation to a DisplayObject (they will be inherited at the binding process) 95 | * 96 | * @param matrix The original matrix (optional) 97 | * @param width The rectangle width (optional) 98 | * @param height The rectangle height (optional) 99 | * @param pivot An absolute point to set the pivot (optional) 100 | **/ 101 | public function new(matrix:Matrix=null,width:Float=0,height:Float=0,?pivot:Point=null) 102 | { 103 | super(); 104 | // set the target and get the original properties 105 | //this.target = target; 106 | if (matrix==null) matrix = new Matrix(); 107 | _matrix = matrix; 108 | _identityWidth = _width = width; 109 | _identityHeight = _height = height; 110 | _identityX = _matrix.tx; 111 | _identityY = _matrix.ty; 112 | spriteDebug = new Shape(); 113 | 114 | if (pivot==null) { 115 | //set the pivot point TOPLEFT of the target if nothing specified 116 | setAnchoredPivot(Transformation.ANCHOR_TOP_LEFT); 117 | } else { 118 | //set the pivot point to the specified coords 119 | setPivot(pivot); 120 | } 121 | } 122 | 123 | public function destroy(){ 124 | this.spriteDebug = null; 125 | this._matrix = null; 126 | this.release(); 127 | this.offsetPoint = null; 128 | } 129 | 130 | // TARGET 131 | // ######################################################################### 132 | 133 | /** 134 | * Bind the transformation matrix to a target (has to be a DisplayObject) 135 | **/ 136 | public function bind(target:DisplayObject){ 137 | this._target = target; 138 | this.x = target.x; 139 | this.y = target.y; 140 | this.updateSize(); 141 | } 142 | 143 | private function release(){ 144 | this._target = null; 145 | } 146 | 147 | /** 148 | * When the size is changed externally of the Transformation class 149 | * you should use this method to update the size internally 150 | * 151 | * **example:** a textfield object with text changed will change size 152 | * but the Transformation class can't be aware of that if you don't 153 | * call this method 154 | * 155 | * ---- 156 | * 157 | * Otherwise you can set the size to arbitrary values if you want to 158 | * set a certain AnchoredPivot based on the rect you specify 159 | * 160 | **/ 161 | public function updateSize(?nw:Float=0,?nh:Float=0) { 162 | /* */ 163 | 164 | // get current translation and the complete matrix 165 | var translation:Point = getTranslation(); 166 | var currentMatrix:Matrix = getMatrixInternal(); 167 | 168 | // remove all transformation 169 | this.identity(); 170 | 171 | // get the _identity width and height without transformations 172 | if ((nw==0 || nh==0)&&(_target != null)) { 173 | _identityWidth = _target.width; 174 | _identityHeight = _target.height; 175 | } else { 176 | _identityWidth = nw; 177 | _identityHeight = nh; 178 | } 179 | 180 | // reset the anchored pivot (based on new size) 181 | if (this.pivotPointAnchor!=-1) setAnchoredPivot(this.pivotPointAnchor); 182 | 183 | // restore the transformation 184 | this.setMatrixInternal(currentMatrix); 185 | 186 | // restore the original translation 187 | // (the new given anchored pivot will count) 188 | this.setTranslation(translation); 189 | currentMatrix = null; 190 | translation = null; 191 | } 192 | 193 | 194 | 195 | // EVENTS 196 | // ######################################################################### 197 | 198 | /** 199 | * The transform event 200 | * It will be called every time the class change the transformation matrix 201 | * 202 | * **example:** 203 | * ``` 204 | * myTransformation.addEventListener(Transformation.TRANSFORM, onMyTransform); 205 | * ``` 206 | **/ 207 | public static inline var TRANSFORM:String = "TRANSFORM"; 208 | 209 | /** 210 | * The pivot change event 211 | * It will be called every time the class change the pivot point 212 | * 213 | * **example:** 214 | * ``` 215 | * myTransformation.addEventListener(Transformation.PIVOT_CHANGE, onMyPivotChange); 216 | * ``` 217 | **/ 218 | public static inline var PIVOT_CHANGE:String = "PIVOT_CHANGE"; 219 | 220 | private function onTransform(){ 221 | dispatchEvent(new Event(TRANSFORM)); 222 | } 223 | 224 | private function onPivotChange(){ 225 | dispatchEvent(new Event(PIVOT_CHANGE)); 226 | } 227 | 228 | 229 | 230 | // STATIC UTILS 231 | // ######################################################################### 232 | 233 | /** 234 | * Calculate the distance between two points 235 | * 236 | * @param p0 First point 237 | * @param p1 Second point 238 | * @return The distance 239 | **/ 240 | public static inline function distance(p0:Point, p1:Point) : Float 241 | { 242 | var x = p0.x-p1.x; 243 | var y = p0.y-p1.y; 244 | return Math.sqrt(x*x + y*y); 245 | } 246 | 247 | 248 | 249 | // DEBUG 250 | // ######################################################################### 251 | 252 | /** 253 | * Clear the spriteDebug:Sprite object 254 | **/ 255 | public function debugClear() { 256 | spriteDebug.graphics.clear(); 257 | } 258 | /** 259 | * Draw in the spriteDebug:Sprite object 260 | * 261 | * Useful to be called with the event TRANSFORM to have a graphic representation 262 | * of the ongoing transformation 263 | * 264 | * **example:** 265 | * ```haxe 266 | * // new 267 | * myspriteTrs = new Transformation(mysprite); 268 | * myspriteTrs.addEventListener(Transformation.TRANSFORM, onTransform); 269 | * addChild(myspriteTrs.debugDraw); 270 | * 271 | * //[...] 272 | * 273 | * public function onTransform(event:Event) { 274 | * myspriteTrs.debugDraw(); 275 | * } 276 | * ``` 277 | * 278 | * @param drawPivot Draws the pivot point flag 279 | * @param drawOrigin Draws the point 0,0 transformed (top left edge of the original rect) 280 | * @param drawOriginal Draws the original rect untransformed 281 | * @param drawBoundaries Draws the rect that enclose the transformed object 282 | * @param drawRotation Draws a circle in the pivot point with radius distance(pivotPoint, point 0,0 transformed) 283 | **/ 284 | public function debugDraw( drawPivot:Bool=true, 285 | drawOrigin:Bool=true, 286 | drawOriginal:Bool=true, 287 | drawBoundaries:Bool=true, 288 | drawRotation:Bool=true) { 289 | 290 | debugClear(); 291 | 292 | if (_target==null) { 293 | 294 | } 295 | 296 | var pivot:Point = getPivot(); 297 | 298 | // p0 .___. p1 299 | // | | 300 | // p2 .___. p3 301 | 302 | var p0:Point = new Point(0,0); 303 | if (drawOrigin || drawBoundaries) { 304 | p0 = getPosition(); 305 | } 306 | 307 | 308 | // pivot point 309 | if (drawPivot) { 310 | spriteDebug.graphics.beginFill(0x00FF00,1); 311 | spriteDebug.graphics.drawCircle(pivot.x,pivot.y,5); 312 | spriteDebug.graphics.endFill(); 313 | } 314 | 315 | // original 0,0 transformed 316 | if (drawOrigin) { 317 | spriteDebug.graphics.beginFill(0xFFFF00,0.5); 318 | spriteDebug.graphics.drawCircle(p0.x,p0.y,5); 319 | spriteDebug.graphics.endFill(); 320 | } 321 | 322 | // original target boundaries (same as original target rect) 323 | if (drawOriginal) { 324 | spriteDebug.graphics.lineStyle(2, 0x0000FF, .5, false); 325 | spriteDebug.graphics.drawRect(_identityX,_identityY,_identityWidth,_identityHeight); 326 | } 327 | 328 | // transformed target boundaries 329 | if (drawBoundaries) { 330 | var p1:Point; 331 | p1 = transformPoint(new Point(_identityWidth,0)); 332 | var p2:Point; 333 | p2 = transformPoint(new Point(0,_identityHeight)); 334 | var p3:Point; 335 | p3 = transformPoint(new Point(_identityWidth,_identityHeight)); 336 | var _identityZeroX = Math.min(Math.min(Math.min(p0.x,p1.x),p2.x),p3.x); 337 | var _identityZeroY = Math.min(Math.min(Math.min(p0.y,p1.y),p2.y),p3.y); 338 | _width = Math.max(Math.max(Math.max(p0.x,p1.x),p2.x),p3.x)-_identityZeroX; 339 | _height = Math.max(Math.max(Math.max(p0.y,p1.y),p2.y),p3.y)-_identityZeroY; 340 | spriteDebug.graphics.lineStyle(2, 0xFF00FF, .5, false); 341 | spriteDebug.graphics.drawRect(_identityZeroX,_identityZeroY,_width,_height); 342 | } 343 | 344 | // rotation circle 345 | if (drawRotation) { 346 | spriteDebug.graphics.lineStyle(2, 0x00FF00, .5, false); 347 | spriteDebug.graphics.drawCircle(pivot.x,pivot.y,distance(pivot,p0)); 348 | } 349 | } 350 | 351 | 352 | // PIVOT MANAGMENT 353 | // ######################################################################### 354 | 355 | // Pivot Point Anchors 356 | // ------------------- 357 | // 358 | // 0,0 - 1,0 - 2,0 x=> LEFT=0 CENTER=1 RIGHT=2 359 | // | | | y=> TOP=0 MIDDLE=1 BOTTOM=2 360 | // 0,1 - 1,1 - 2,1 361 | // | | | 362 | // 0,2 - 1,2 - 2,2 363 | // 364 | 365 | /** 366 | * Set the pivot in the Pivot Point Anchor specified (see the constants ANCHOR_*) 367 | * 368 | * @param pivotPointAnchor A pivot point anchor 369 | **/ 370 | public function setAnchoredPivot(pivotPointAnchor:Int) { 371 | //set the Pivot Point Anchor as specified 372 | this.pivotPointAnchor = pivotPointAnchor; 373 | //set the pivot offset based on the Pivot Point Anchor specified 374 | offsetPoint = getAnchoredPivotOffset(pivotPointAnchor); 375 | this.onPivotChange(); 376 | } 377 | 378 | /** 379 | * Set the pivot in an arbitrary point 380 | * 381 | * @param point A point 382 | **/ 383 | public function setPivot(point:Point) { 384 | //unset the Pivot Point Anchor 385 | this.pivotPointAnchor = -1; 386 | //set the pivot offset 387 | offsetPoint = inverseTransformPoint(point); 388 | this.onPivotChange(); 389 | } 390 | 391 | /** 392 | * Set the pivot offset (from target 0,0) in an arbitrary point 393 | * 394 | * @param point A point 395 | **/ 396 | public function setPivotOffset(point:Point) { 397 | //unset the Pivot Point Anchor 398 | this.pivotPointAnchor = -1; 399 | //set the pivot offset 400 | offsetPoint = point; 401 | this.onPivotChange(); 402 | } 403 | 404 | /** 405 | * Get the pivot absolute position 406 | * 407 | * @returns The pivot point absolute position 408 | **/ 409 | public function getPivot():Point { 410 | return transformPoint(offsetPoint); 411 | } 412 | 413 | /** 414 | * Get the pivot offset position (from target 0,0) 415 | * 416 | * @returns The pivot point offset position 417 | **/ 418 | public function getPivotOffset():Point { 419 | return offsetPoint; 420 | } 421 | 422 | /** 423 | * Get the offset (from target 0,0) position of a specified Pivot Point Anchor (see the constants ANCHOR_*) 424 | * 425 | * @return The point position 426 | **/ 427 | public function getAnchoredPivotOffset(?pivotPointAnchor:Int=0):Point { 428 | return getAnchorPivot(pivotPointAnchor,false); 429 | } 430 | 431 | /** 432 | * Get the absolute position of a specified Pivot Point Anchor (see the constants ANCHOR_*) 433 | * 434 | * @return The point position 435 | **/ 436 | public function getAnchoredPivot(?pivotPointAnchor:Int=0):Point { 437 | return getAnchorPivot(pivotPointAnchor,true); 438 | } 439 | 440 | // internal (used by getAnchoredPivotOffset and getAnchoredPivot) 441 | private function getAnchorPivot(pivotPointAnchor:Int,absolute:Bool):Point { 442 | // _identityWidth / 2 * 0 is LEFT .______ 443 | // _identityWidth / 2 * 1 is CENTER ___.___ 444 | // _identityWidth / 2 * 2 is RIGHT ______. 445 | // and so on for the Y 446 | if (pivotPointAnchor<0 || pivotPointAnchor>8) pivotPointAnchor = 0; 447 | var pivotPositionX = pivotPointAnchor % 3; 448 | var pivotPositionY = Std.int(pivotPointAnchor / 3); 449 | 450 | var x = _identityWidth/2*pivotPositionX; 451 | var y = _identityHeight/2*pivotPositionY; 452 | 453 | // add the current translation to the point 454 | // to get the absolute position 455 | if (absolute) { 456 | var translation = getPosition(); 457 | x+=translation.x; // pos.x is matrix.tx 458 | y+=translation.y; // pos.y is matrix.ty 459 | } 460 | return new Point(x,y); 461 | } 462 | 463 | // ######################################################################### 464 | 465 | 466 | private function setMatrixInternal(m:Matrix,?adjust=false):Void 467 | { 468 | // adjust==true is needed to respect the Pivot Point 469 | var originalPoint:Point = new Point(0,0); 470 | if (adjust) originalPoint = getPivot(); //this before apply the transform 471 | _matrix = m; //apply the transformation 472 | m = null; 473 | if (adjust) adjustOffset(originalPoint); //this after apply the transform 474 | if (_target != null) this._target.transform.matrix = getMatrixInternal(); 475 | this.onTransform(); 476 | } 477 | 478 | /** 479 | * Set the transformation matrix, overwriting the existing one, 480 | * using the specified values 481 | * 482 | * (the pivot point will be respected) 483 | **/ 484 | public function setMatrixTo(a:Float,b:Float,c:Float,d:Float,tx:Float,ty:Float) { 485 | // update the matrix and adjust to respect the Pivot Point 486 | setMatrixInternal(new Matrix(a,b,c,d,tx,ty),true); 487 | } 488 | 489 | private function getMatrixInternal():Matrix 490 | { 491 | return _matrix.clone(); 492 | } 493 | 494 | // ######################################################################### 495 | 496 | 497 | private function adjustOffset(originalPoint:Point) { 498 | 499 | //get the target matrix to apply the transformation 500 | var m:Matrix = getMatrixInternal(); 501 | 502 | //get the pivot NEW absolute position 503 | var transformedPoint:Point = m.transformPoint(offsetPoint); 504 | // get the Pivot position offset between before and after the transformation 505 | var offset:Point = new Point(transformedPoint.x-originalPoint.x, 506 | transformedPoint.y-originalPoint.y); 507 | 508 | // apply the offset with a translation to the target 509 | // to keep the pivot relative position coherent 510 | m.tx-=offset.x; 511 | m.ty-=offset.y; 512 | 513 | //apply the matrix to the target 514 | setMatrixInternal(m); 515 | 516 | } 517 | 518 | 519 | 520 | // POINT TRANSFORMATIONS 521 | // ######################################################################### 522 | 523 | /** 524 | * Transform a point using the current transformation matrix 525 | * 526 | * @param point The point to be transformed 527 | * @returns The transformed point 528 | **/ 529 | public function transformPoint(point:Point):Point { 530 | // apply the current transformation on a point 531 | return _matrix.transformPoint(point); 532 | } 533 | 534 | /** 535 | * Transform a point using the current transformation matrix 536 | * but ignoring the translation 537 | * 538 | * @param point The point to be transformed 539 | * @returns The transformed point 540 | **/ 541 | public function deltaTransformPoint(point:Point):Point { 542 | // apply the current transformation on a point 543 | // [ignore the translation] 544 | return _matrix.deltaTransformPoint(point); 545 | } 546 | 547 | /** 548 | * Transform a point using the inverted current transformation matrix 549 | * This means: 550 | * ``` 551 | * p == inverseTransformPoint(transformPoint(p)); // is true 552 | * ``` 553 | * 554 | * @param point The point to be transformed 555 | * @returns The transformed point 556 | **/ 557 | public function inverseTransformPoint(point:Point):Point { 558 | // remove the current transformation on a point 559 | // (give a transformed point to get a 'identity' point) 560 | var m:Matrix = getMatrixInternal(); 561 | m.invert(); 562 | return m.transformPoint(point); 563 | } 564 | 565 | /** 566 | * Transform a point using the inverted current transformation matrix 567 | * but ignoring the translation 568 | * This means: 569 | * ``` 570 | * p == inverseDeltaTransformPoint(deltaTransformPoint(p)); // is true 571 | * ``` 572 | * 573 | * @param point The point to be transformed 574 | * @returns The transformed point 575 | **/ 576 | public function inverseDeltaTransformPoint(point:Point):Point { 577 | // remove the current transformation on a point 578 | // (give a transformed point to get a 'identity' point) 579 | // [ignore the translation] 580 | var m:Matrix = getMatrixInternal(); 581 | m.invert(); 582 | return m.deltaTransformPoint(point); 583 | } 584 | 585 | 586 | 587 | // IDENTITY 588 | // ######################################################################### 589 | 590 | /** 591 | * Reset the matrix (no transformations) 592 | * 593 | * (the pivot point will be respected) 594 | **/ 595 | public function identity(){ 596 | // reset the matrix 597 | var m = new Matrix(); 598 | m.tx = _identityX - offsetPoint.x; 599 | m.ty = _identityY - offsetPoint.y; 600 | setMatrixInternal(m); 601 | } 602 | 603 | 604 | 605 | // TRANSLATE TRANSFORMATION 606 | // ######################################################################### 607 | 608 | /** 609 | * Delta translation 610 | * @param dx translation on the X axis 611 | * @param dy translation on the Y axis 612 | **/ 613 | public function translate(dx:Float=0, dy:Float=0):Void 614 | { 615 | var m:Matrix = getMatrixInternal(); 616 | m.tx += dx; 617 | m.ty += dy; 618 | setMatrixInternal(m); 619 | } 620 | 621 | /** 622 | * Delta translation on X axis 623 | * @param dx translation on the X axis 624 | **/ 625 | public function translateX(dx:Float=0):Void { translate(dx,0); } 626 | 627 | /** 628 | * Delta translation on Y axis 629 | * @param dy translation on the Y axis 630 | **/ 631 | public function translateY(dy:Float=0):Void { translate(0,dy); } 632 | 633 | /** 634 | * Absolute translation 635 | * @param translation The specified point 636 | **/ 637 | public function setTranslation(translation:Point):Void 638 | { 639 | var m:Matrix = getMatrixInternal(); 640 | var transformedOffset:Point = deltaTransformPoint(offsetPoint); 641 | m.tx = translation.x-transformedOffset.x; 642 | m.ty = translation.y-transformedOffset.y; 643 | setMatrixInternal(m); 644 | } 645 | /** 646 | * Absolute translation on the X axis 647 | * @param tx The X coordinate 648 | **/ 649 | public function setTranslationX(tx:Float=0):Float { 650 | var m:Matrix = getMatrixInternal(); 651 | m.tx = tx-deltaTransformPoint(offsetPoint).x; 652 | setMatrixInternal(m); 653 | return tx; 654 | } 655 | /** 656 | * Absolute translation on the Y axis 657 | * @param ty The Y coordinate 658 | **/ 659 | public function setTranslationY(ty:Float=0):Float { 660 | var m:Matrix = getMatrixInternal(); 661 | m.ty = ty-deltaTransformPoint(offsetPoint).y; 662 | setMatrixInternal(m); 663 | return ty; 664 | } 665 | 666 | /** 667 | * Get the current translation 668 | * @returns The current absolute translation point (relative to 0,0) 669 | **/ 670 | public function getTranslation():Point 671 | { 672 | var transformedOffset:Point = deltaTransformPoint(offsetPoint); 673 | return new Point(_matrix.tx+transformedOffset.x,_matrix.ty+transformedOffset.y); 674 | } 675 | 676 | /** 677 | * Get the current translation on the X axis 678 | * @returns The current absolute translation X coordinate 679 | **/ 680 | public function getTranslationX():Float 681 | { 682 | return getTranslation().x; 683 | } 684 | 685 | /** 686 | * Get the current translation on the Y axis 687 | * @returns The current absolute translation Y coordinate 688 | **/ 689 | public function getTranslationY():Float 690 | { 691 | return getTranslation().y; 692 | } 693 | 694 | /** 695 | * Get the current matrix translation (matrix.tx,matrix.ty) 696 | * (this method ignore the pivot point) 697 | * @returns The current matrix translation Point(matrix.tx,matrix.ty) 698 | **/ 699 | public function getPosition():Point 700 | { 701 | return new Point(_matrix.tx,_matrix.ty); 702 | } 703 | 704 | /** 705 | * Get the current matrix translation on the X axis (matrix.tx) 706 | * (this method ignore the pivot point) 707 | * @returns The current matrix translation matrix.tx 708 | **/ 709 | public function getPositionX():Float 710 | { 711 | return _matrix.tx; 712 | } 713 | 714 | /** 715 | * Get the current matrix translation on the Y axis (matrix.ty) 716 | * (this method ignore the pivot point) 717 | * @returns The current matrix translation matrix.ty 718 | **/ 719 | public function getPositionY():Float 720 | { 721 | return _matrix.ty; 722 | } 723 | 724 | 725 | /** Use getTranslationX and setTranslationX **/ 726 | public var translationX(get, set):Float; 727 | private function get_translationX():Float { return getTranslationX(); } 728 | private function set_translationX(value:Float):Float { return setTranslationX(value); } 729 | 730 | /** Use getTranslationY and setTranslationY **/ 731 | public var translationY(get, set):Float; 732 | private function get_translationY():Float { return getTranslationY(); } 733 | private function set_translationY(value:Float):Float { return setTranslationY(value); } 734 | 735 | 736 | /** Use getTranslationX and setTranslationX **/ 737 | public var x(get, set):Float; 738 | private function get_x():Float { return getTranslationX(); } 739 | private function set_x(value:Float):Float { return setTranslationX(value); } 740 | 741 | /** Use getTranslationY and setTranslationY **/ 742 | public var y(get, set):Float; 743 | private function get_y():Float { return getTranslationY(); } 744 | private function set_y(value:Float):Float { return setTranslationY(value); } 745 | 746 | 747 | public var width(get, never):Float; 748 | private function get_width():Float { return _width; } 749 | //private function set_width(value:Float):Float { return _width; } 750 | 751 | public var height(get, never):Float; 752 | private function get_height():Float { return _height; } 753 | //private function set_height(value:Float):Float { return _height; } 754 | 755 | 756 | 757 | // SKEW TRANSFORMATION 758 | // ######################################################################### 759 | 760 | /** 761 | * Set the skew to a given value in Radians 762 | * 763 | * **NOTE:** It applies an absolute skew on the current matrix 764 | * 765 | * **NOTE:** -0.1 is a null value for cross-platform compatibility (doesn't apply the transformation) 766 | * 767 | * @param skewXRad Value for the X axis 768 | * @param skewXRad Value for the Y axis 769 | **/ 770 | public function setSkewRad(skewXRad:Float, skewYRad:Float):Void 771 | { 772 | 773 | //get the target matrix to apply the transformation 774 | var m:Matrix = getMatrixInternal(); 775 | 776 | // apply the skew (matrix.c is HORIZONTAL, matrix.b is VERTICAL) 777 | if (skewXRad!=NULL) { 778 | m.c = Math.tan(skewXRad)*getScaleX(); 779 | } 780 | if (skewYRad!=NULL) { 781 | m.b = Math.tan(skewYRad)*getScaleY(); 782 | } 783 | 784 | //apply the matrix to the target 785 | setMatrixInternal(m,true); 786 | } 787 | 788 | /** 789 | * Set the skew to a given value in Degrees 790 | * 791 | * **NOTE:** It applies an absolute skew on the current matrix 792 | * 793 | * **NOTE:** -0.1 is a null value for cross-platform compatibility (doesn't apply the transformation) 794 | * 795 | * @param skewXDeg Value for the X axis 796 | * @param skewYDeg Value for the Y axis 797 | **/ 798 | public function setSkew(skewXDeg:Float, skewYDeg:Float):Void 799 | { 800 | // check null to avoid error on multiplication 801 | var skewXRad:Float=NULL; 802 | var skewYRad:Float=NULL; 803 | if (skewXDeg!=NULL) skewXRad = skewXDeg*DEG2RAD; 804 | if (skewYDeg!=NULL) skewYRad = skewYDeg*DEG2RAD; 805 | setSkewRad(skewXRad,skewYRad); 806 | } 807 | 808 | /** 809 | * Set the skew on the X axis to a given value in Degrees 810 | * 811 | * **NOTE:** It applies an absolute skew on the current matrix 812 | * 813 | * @param skewXDeg Value for the X axis 814 | **/ 815 | public function setSkewX(skewXDeg:Float=NULL):Float { setSkew(skewXDeg,NULL); return skewXDeg; } 816 | 817 | /** 818 | * Set the skew on the Y axis to a given value in Degrees 819 | * 820 | * **NOTE:** It applies an absolute skew on the current matrix 821 | * 822 | * @param skewYDeg Value for the Y axis 823 | **/ 824 | public function setSkewY(skewYDeg:Float):Float { setSkew(NULL,skewYDeg); return skewYDeg; } 825 | 826 | /** 827 | * Set the skew on the X axis to a given value in Radians 828 | * 829 | * **NOTE:** It applies an absolute skew on the current matrix 830 | * 831 | * @param skewXRad Value for the X axis 832 | **/ 833 | public function setSkewXRad(skewXRad:Float):Float { setSkewRad(skewXRad,NULL); return skewXRad; } 834 | 835 | /** 836 | * Set the skew on the Y axis to a given value in Radians 837 | * 838 | * **NOTE:** It applies an absolute skew on the current matrix 839 | * 840 | * @param skewYRad Value for the X axis 841 | **/ 842 | public function setSkewYRad(skewYRad:Float):Float { setSkewRad(NULL,skewYRad); return skewYRad; } 843 | 844 | /** 845 | * Apply a skew in Radians 846 | * 847 | * **NOTE:** -0.1 for skewYRad is a null value for cross-platform compatibility (apply the same value on X and Y) 848 | * **NOTE:** 0.0 means no transformation on that axis 849 | * 850 | * @param skewRad Value for the X axis (and Y axis if the skewY is not specified) 851 | * @param skewYRad (optional) Value for the Y axis 852 | **/ 853 | public function skewRad(skewRad:Float, ?skewYRad:Float=NULL):Void 854 | { 855 | //get the target matrix to apply the transformation 856 | var m:Matrix = new Matrix(); 857 | 858 | var skewXRad:Float = skewRad; 859 | // if not specified it will set the x and y skew using the same value 860 | if (skewYRad==NULL) skewYRad = skewRad; 861 | 862 | // apply the skew (matrix.c is HORIZONTAL, matrix.b is VERTICAL) 863 | if (skewXRad!=0.0) { 864 | m.c = Math.tan(skewXRad); 865 | } 866 | if (skewYRad!=0.0) { 867 | m.b = Math.tan(skewYRad); 868 | } 869 | 870 | //apply the matrix to the target 871 | m.concat(getMatrixInternal()); 872 | setMatrixInternal(m,true); 873 | } 874 | 875 | /** 876 | * Apply a skew in Degrees 877 | * 878 | * **NOTE:** -0.1 for skewYDeg is a null value for cross-platform compatibility (apply the same value on X and Y) 879 | * **NOTE:** 0.0 means no transformation on that axis 880 | * 881 | * @param skewDeg Value for the X axis (and Y axis if the skewY is not specified) 882 | * @param skewYDeg (optional) Value for the Y axis 883 | **/ 884 | public function skew(skewDeg:Float,?skewYDeg:Float=NULL):Void { 885 | var skewXDeg:Float = skewDeg; 886 | // if not specified it will set the x and y skew using the same value 887 | if (skewYDeg==NULL) skewYDeg = skewDeg; 888 | skewRad(skewXDeg*DEG2RAD,skewYDeg*DEG2RAD); 889 | } 890 | 891 | /** 892 | * Apply a skew on the X axis in Degrees 893 | * 894 | * @param skewXDeg Value for the skew 895 | **/ 896 | public function skewX(skewXDeg:Float):Void { skew(skewXDeg,0.0); } 897 | 898 | /** 899 | * Apply a skew on the X axis in Degrees 900 | * 901 | * @param skewYDeg Value for the skew 902 | **/ 903 | public function skewY(skewYDeg:Float):Void { skew(0.0,skewYDeg); } 904 | 905 | /** 906 | * Apply a skew on the X axis in Radians 907 | * 908 | * @param skewXRad Value for the skew 909 | **/ 910 | public function skewXRad(skewXRad:Float):Void { skewRad(skewXRad,0.0); } 911 | 912 | /** 913 | * Apply a skew on the Y axis in Radians 914 | * 915 | * @param skewYRad Value for the skew 916 | **/ 917 | public function skewYRad(skewYRad:Float):Void { skewRad(0.0,skewYRad); } 918 | 919 | /** 920 | * **NOTE:** Could not be reliable if the scale is not uniform 921 | * 922 | * @returns the current skew on the X Axis in Radians 923 | **/ 924 | public function getSkewXRad():Float 925 | { 926 | point.x = 0; point.y = 1; 927 | point = deltaTransformPoint(point); 928 | return -(Math.atan2(point.y, point.x) - Math.PI/2); 929 | } 930 | 931 | /** 932 | * **NOTE:** Could not be reliable if the scale is not uniform 933 | * 934 | * @returns the current skew on the Y Axis in Radians 935 | **/ 936 | public function getSkewYRad():Float 937 | { 938 | point.x = 1; point.y = 0; 939 | point = deltaTransformPoint(point); 940 | return Math.atan2(point.y, point.x); 941 | } 942 | 943 | /** 944 | * **NOTE:** Could not be reliable if the scale is not uniform 945 | * 946 | * @returns the current skew on the X Axis in Degrees 947 | **/ 948 | public function getSkewX():Float { return getSkewXRad()*RAD2DEG; } 949 | 950 | /** 951 | * **NOTE:** Could not be reliable if the scale is not uniform 952 | * 953 | * @returns the current skew on the Y Axis in Degrees 954 | **/ 955 | public function getSkewY():Float { return getSkewYRad()*RAD2DEG; } 956 | 957 | /** Use getSkewX and setSkewX **/ 958 | public var skewingX(get, set):Float; 959 | private function get_skewingX():Float { return getSkewX(); } 960 | private function set_skewingX(value:Float):Float { return setSkewX(value); } 961 | 962 | /** Use getSkewY and setSkewY **/ 963 | public var skewingY(get, set):Float; 964 | private function get_skewingY():Float { return getSkewY(); } 965 | private function set_skewingY(value:Float):Float { return setSkewY(value); } 966 | 967 | /** Use getSkewXRad and setSkewXRad **/ 968 | public var skewingXRad(get, set):Float; 969 | private function get_skewingXRad():Float { return getSkewXRad(); } 970 | private function set_skewingXRad(value:Float):Float { return setSkewXRad(value); } 971 | 972 | /** Use getSkewYRad and setSkewYRad **/ 973 | public var skewingYRad(get, set):Float; 974 | private function get_skewingYRad():Float { return getSkewYRad(); } 975 | private function set_skewingYRad(value:Float):Float { return setSkewYRad(value); } 976 | 977 | 978 | // SCALE TRANSFORMATION 979 | // ######################################################################### 980 | 981 | private var _currentScaleX:Float = 1; 982 | private var _currentScaleY:Float = 1; 983 | 984 | /** 985 | * Apply a scale 986 | * 987 | * **NOTE:** yfactor=-0.1 is a null value for cross-platform compatibility (apply the same factor on x and y) 988 | * 989 | * @param factor Factor for the X axis (and Y axis if the yFactor is not specified) 990 | * @param yFactor (optional) Factor for the Y axis 991 | **/ 992 | public function scale(factor:Float=1.0, ?yFactor:Float=NULL):Void 993 | { 994 | 995 | var xFactor:Float = factor; 996 | _currentScaleX*=xFactor; 997 | // if not specified it will scale x and y using the same factor 998 | if (yFactor==NULL) yFactor = factor; 999 | _currentScaleY*=yFactor; 1000 | 1001 | //get the pivot absolute position 1002 | // (keep this BEFORE applying the new matrix to the target) 1003 | 1004 | //get the target matrix to apply the transformation 1005 | var m:Matrix = getMatrixInternal(); 1006 | 1007 | // apply the scaling 1008 | m.a *= xFactor; 1009 | m.b *= yFactor; 1010 | m.c *= xFactor; 1011 | m.d *= yFactor; 1012 | m.tx *= xFactor; 1013 | m.ty *= yFactor; 1014 | 1015 | //apply the matrix to the target 1016 | setMatrixInternal(m,true); 1017 | 1018 | } 1019 | 1020 | /** 1021 | * Apply a scale on the X axis 1022 | * 1023 | * @param factor Factor for the X axis 1024 | **/ 1025 | public function scaleX(factor:Float=1):Void { scale(factor,1.0); } 1026 | 1027 | /** 1028 | * Apply a scale on the Y axis 1029 | * 1030 | * @param factor Factor for the Y axis 1031 | **/ 1032 | public function scaleY(factor:Float=1):Void { scale(1.0,factor); } 1033 | 1034 | /** 1035 | * Set the scale on a given value 1036 | * 1037 | * **NOTE:** scaleY=-0.1 is a null value for cross-platform compatibility (use the same value for x and y) 1038 | * 1039 | * @param value Value for the X axis (and Y axis if the scaleY is not specified) 1040 | * @param scaleY (optional) Value for the Y axis 1041 | **/ 1042 | public function setScale(value:Float=1.0, ?scaleY:Float=NULL):Float 1043 | { 1044 | 1045 | var scaleX:Float = value; 1046 | // if not specified it will set the x and y scale using the same value 1047 | if (scaleY==NULL) scaleY = value; 1048 | 1049 | //apply the transformation 1050 | setScaleX(scaleX); 1051 | setScaleY(scaleY); 1052 | 1053 | return value; 1054 | 1055 | } 1056 | 1057 | /** 1058 | * Set the scale for the X axis on a given value 1059 | * 1060 | * @param value Value for the X axis 1061 | **/ 1062 | public function setScaleX(scaleX:Float):Float 1063 | { 1064 | var m:Matrix = getMatrixInternal(); 1065 | var oldValue:Float = getScaleX(); 1066 | // avoid division by zero 1067 | if (oldValue!=0) 1068 | { 1069 | var ratio:Float = scaleX / oldValue; 1070 | m.a *= ratio; 1071 | m.b *= ratio; 1072 | } else { 1073 | var skewYRad:Float = getSkewYRad(); 1074 | m.a = Math.cos(skewYRad) * scaleX; 1075 | m.b = Math.sin(skewYRad) * scaleX; 1076 | } 1077 | setMatrixInternal(m,true); 1078 | _currentScaleX = scaleX; 1079 | return scaleX; 1080 | } 1081 | 1082 | /** 1083 | * Set the scale for the Y axis on a given value 1084 | * 1085 | * @param value Value for the Y axis 1086 | **/ 1087 | public function setScaleY(scaleY:Float):Float 1088 | { 1089 | var m:Matrix = getMatrixInternal(); 1090 | var oldValue:Float = getScaleY(); 1091 | // avoid division by zero 1092 | if (oldValue!=0) 1093 | { 1094 | var ratio:Float = scaleY / oldValue; 1095 | m.c *= ratio; 1096 | m.d *= ratio; 1097 | } 1098 | else 1099 | { 1100 | var skewXRad:Float = getSkewXRad(); 1101 | m.c = -Math.sin(skewXRad) * scaleY; 1102 | m.d = Math.cos(skewXRad) * scaleY; 1103 | } 1104 | setMatrixInternal(m,true); 1105 | _currentScaleY = scaleY; 1106 | return scaleY; 1107 | } 1108 | 1109 | /** 1110 | * @returns The current scale factor on the X axis 1111 | **/ 1112 | public function getScaleX():Float 1113 | { 1114 | return _currentScaleX; 1115 | //return Math.sqrt(_matrix.a*_matrix.a + _matrix.b*_matrix.b); 1116 | } 1117 | /** 1118 | * @returns The current scale factor on the Y axis 1119 | **/ 1120 | public function getScaleY():Float 1121 | { 1122 | return _currentScaleY; 1123 | //return Math.sqrt(_matrix.c*_matrix.c + _matrix.d*_matrix.d); 1124 | } 1125 | 1126 | /** Use getScale and setScale **/ 1127 | public var scaling(get, set):Float; 1128 | private function get_scaling():Float { return getScaleX(); } 1129 | private function set_scaling(factor:Float):Float { return setScale(factor); } 1130 | 1131 | /** Use getScaleX and setScaleX **/ 1132 | public var scalingX(get, set):Float; 1133 | private function get_scalingX():Float { return getScaleX(); } 1134 | private function set_scalingX(factor:Float):Float { return setScaleX(factor); } 1135 | 1136 | // /** Use getScaleY and setScaleY **/ 1137 | public var scalingY(get, set):Float; 1138 | private function get_scalingY():Float { return getScaleY(); } 1139 | private function set_scalingY(factor:Float):Float { return setScaleY(factor); } 1140 | 1141 | 1142 | 1143 | // FLIP 1144 | // ######################################################################### 1145 | 1146 | /** 1147 | * Apply a flip (mirroring) on the X axis 1148 | **/ 1149 | public function flipX():Void { scaleX(-1); } 1150 | 1151 | /** 1152 | * Apply a flip (mirroring) on the Y axis 1153 | **/ 1154 | public function flipY():Void { scaleY(-1); } 1155 | 1156 | 1157 | // ROTATE TRANSFORMATION 1158 | // ######################################################################### 1159 | 1160 | /** 1161 | * Apply a rotation 1162 | * 1163 | * @param angle The angle in Radians 1164 | **/ 1165 | public function rotateRad(angle:Float=0):Void 1166 | { 1167 | //get the pivot absolute position 1168 | var absolutePoint:Point = getPivot(); 1169 | 1170 | //get the target matrix to apply the transformation 1171 | var m:Matrix = getMatrixInternal(); 1172 | 1173 | //move the target(matrix) 1174 | //the pivot point will match the origin (0,0) 1175 | m.tx-=absolutePoint.x; 1176 | m.ty-=absolutePoint.y; 1177 | 1178 | //rotate the target(matrix) 1179 | // SAME AS m.rotate(angle); 1180 | var sin = Math.sin(angle); 1181 | var cos = Math.cos(angle); 1182 | var a = m.a; 1183 | var b = m.b; 1184 | var c = m.c; 1185 | var d = m.d; 1186 | var tx = m.tx; 1187 | var ty = m.ty; 1188 | m.a = a*cos - b*sin; 1189 | m.b = a*sin + b*cos; 1190 | m.c = c*cos - d*sin; 1191 | m.d = c*sin + d*cos; 1192 | m.tx = tx*cos - ty*sin; 1193 | m.ty = tx*sin + ty*cos; 1194 | 1195 | // restore the target(matrix) position 1196 | m.tx+=absolutePoint.x; 1197 | m.ty+=absolutePoint.y; 1198 | 1199 | //apply the matrix to the target 1200 | setMatrixInternal(m); 1201 | } 1202 | 1203 | /** 1204 | * Apply a rotation 1205 | * 1206 | * @param angle The angle in Degrees 1207 | **/ 1208 | public function rotate(angle:Float):Void { rotateRad(angle*DEG2RAD); } 1209 | 1210 | /** 1211 | * Set the rotation on a given value 1212 | * 1213 | * @param angle The absolute angle in Radians 1214 | **/ 1215 | public function setRotationRad(angle:Float):Float 1216 | { 1217 | //get the current angle 1218 | var currentRotation:Float = getRotationRad(); 1219 | 1220 | //find the complementary angle to reset the rotation to 0 1221 | var resetAngle:Float = -currentRotation; 1222 | 1223 | //reset the rotation 1224 | rotateRad(resetAngle); 1225 | 1226 | //set the new rotation value 1227 | rotateRad(angle); 1228 | 1229 | return angle; 1230 | } 1231 | 1232 | /** 1233 | * Set the rotation on a given value 1234 | * 1235 | * @param angle The absolute angle in Degrees 1236 | **/ 1237 | public function setRotation(angle:Float):Float { return setRotationRad(angle*DEG2RAD); } 1238 | 1239 | /** 1240 | * @returns The current angle of rotation in Radians 1241 | **/ 1242 | public function getRotationRad():Float 1243 | { 1244 | 1245 | // apply the transformation matrix to a point and 1246 | // calculate the rotation happened 1247 | // thanks to http://stackoverflow.com/users/1035293/bugshake 1248 | var scale:Float; 1249 | 1250 | var m:Matrix = getMatrixInternal(); 1251 | 1252 | // extract translation 1253 | point.x = point.y = 0; 1254 | point2 = m.transformPoint(point); 1255 | m.translate( -point2.x, -point2.y); 1256 | 1257 | // extract (uniform) scale... 1258 | point.x = 1; point.y = 0; 1259 | point = m.transformPoint(point); 1260 | scale = point.length; 1261 | 1262 | // ...and rotation 1263 | return Math.atan2(point.y, point.x); 1264 | } 1265 | 1266 | /** 1267 | * @returns The current angle of rotation in Degrees 1268 | **/ 1269 | public function getRotation():Float { return getRotationRad() * RAD2DEG; } 1270 | 1271 | /** Use getRotation and setRotation **/ 1272 | public var rotation(get, set):Float; 1273 | private function get_rotation():Float { return getRotation(); } 1274 | private function set_rotation(angle:Float):Float { return setRotation(angle); } 1275 | 1276 | /** Use getRotationRad and setRotationRad **/ 1277 | public var rotationRad(get, set):Float; 1278 | private function get_rotationRad():Float { return getRotationRad(); } 1279 | private function set_rotationRad(angle:Float):Float { return setRotationRad(angle); } 1280 | 1281 | // ######################################################################### 1282 | // ######################################################################### 1283 | 1284 | } 1285 | -------------------------------------------------------------------------------- /docs/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap.js by @fat & @mdo 3 | * plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js 4 | * Copyright 2012 Twitter, Inc. 5 | * http://www.apache.org/licenses/LICENSE-2.0.txt 6 | */ 7 | !function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a(' -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | function createCookie(name, value, days) { 2 | localStorage.setItem(name, value); 3 | } 4 | 5 | function readCookie(name) { 6 | return localStorage.getItem(name); 7 | } 8 | 9 | function toggleInherited(el) { 10 | var toggle = $(el).closest(".toggle"); 11 | toggle.toggleClass("toggle-on"); 12 | if (toggle.hasClass("toggle-on")) { 13 | $("img", toggle).attr("src", dox.rootPath + "triangle-opened.png"); 14 | } else { 15 | $("img", toggle).attr("src", dox.rootPath + "triangle-closed.png"); 16 | } 17 | } 18 | 19 | function toggleCollapsed(el) { 20 | var toggle = $(el).closest(".expando"); 21 | // console.log(toggle); 22 | toggle.toggleClass("expanded"); 23 | 24 | if (toggle.hasClass("expanded")) { 25 | $("img", toggle).first().attr("src", dox.rootPath + "triangle-opened.png"); 26 | } else { 27 | $("img", toggle).first().attr("src", dox.rootPath + "triangle-closed.png"); 28 | } 29 | updateTreeState(); 30 | } 31 | 32 | function updateTreeState(){ 33 | var states = []; 34 | $("#nav .expando").each(function(i, e){ 35 | states.push($(e).hasClass("expanded") ? 1 : 0); 36 | }); 37 | var treeState = JSON.stringify(states); 38 | createCookie("treeState", treeState); 39 | } 40 | 41 | var filters = {}; 42 | 43 | function selectPlatform(e) { 44 | setPlatform($(e.target).parent().attr("data")); 45 | } 46 | 47 | function selectVersion(e) { 48 | setVersion($(e.target).parent().attr("data")); 49 | } 50 | 51 | function setPlatform(platform) { 52 | selectItem("platform", platform); 53 | 54 | var styles = ".platform { display:none }"; 55 | var platforms = dox.platforms; 56 | 57 | for (var i = 0; i < platforms.length; i++) 58 | { 59 | var p = platforms[i]; 60 | 61 | if (platform == "sys") 62 | { 63 | if (p != "flash" && p != "js") 64 | { 65 | styles += ".platform-" + p + " { display:inherit } "; 66 | } 67 | } 68 | else 69 | { 70 | if (platform == "all" || p == platform) 71 | { 72 | styles += ".platform-" + p + " { display:inherit } "; 73 | } 74 | } 75 | } 76 | 77 | if (platform != "flash" && platform != "js") 78 | { 79 | styles += ".platform-sys { display:inherit } "; 80 | } 81 | 82 | $("#dynamicStylesheet").text(styles); 83 | } 84 | 85 | function setVersion(version) { 86 | selectItem("version", version); 87 | } 88 | 89 | function toggleInheritedFields() { 90 | dox.showInheritedFields = !dox.showInheritedFields; 91 | var display = dox.showInheritedFields ? "block" : "none"; 92 | var style = ".inherited { display: " + display + "; }"; 93 | $("#dynamicStylesheet").text(style); 94 | } 95 | 96 | function selectItem(filter, value) 97 | { 98 | var dropdown = $("#select-" + filter); 99 | var item = $("li[data='"+value+"']", dropdown); 100 | var label = $("a", item).text(); 101 | $(".dropdown-toggle", dropdown).html(label + ''); 102 | $("li.active", dropdown).removeClass("active"); 103 | item.addClass("active"); 104 | createCookie(filter, value); 105 | } 106 | 107 | $(document).ready(function(){ 108 | $("#nav").html(navContent); 109 | var treeState = readCookie("treeState"); 110 | 111 | $("#nav .expando").each(function(i, e){ 112 | $("img", e).first().attr("src", dox.rootPath + "triangle-closed.png"); 113 | }); 114 | 115 | $(".treeLink").each(function() { 116 | this.href = this.href.replace("::rootPath::", dox.rootPath); 117 | }); 118 | 119 | if (treeState != null) 120 | { 121 | var states = JSON.parse(treeState); 122 | $("#nav .expando").each(function(i, e){ 123 | if (states[i]) { 124 | $(e).addClass("expanded"); 125 | $("img", e).first().attr("src", dox.rootPath + "triangle-opened.png"); 126 | } 127 | }); 128 | } 129 | $("#select-platform li a").on("click", selectPlatform); 130 | $("#select-version li a").on("click", selectVersion); 131 | $("head").append(""); 132 | 133 | setPlatform(readCookie("platform") == null ? "all" : readCookie("platform")); 134 | setVersion(readCookie("version") == null ? "3_0" : readCookie("version")); 135 | 136 | $("#search").on("keyup", function(e){ 137 | searchQuery(e.target.value); 138 | }); 139 | }); 140 | 141 | function searchQuery(query) { 142 | query = query.toLowerCase(); 143 | $("#searchForm").removeAttr("action"); 144 | if (query == "") { 145 | $("#nav").removeClass("searching"); 146 | $("#nav li").each(function(index, element){ 147 | var e = $(element); 148 | e.css("display", ""); 149 | }); 150 | return; 151 | } 152 | 153 | console.log("Searching: "+query); 154 | 155 | var searchSet = false; 156 | 157 | $("#nav").addClass("searching"); 158 | $("#nav li").each(function(index, element){ 159 | var e = $(element); 160 | if (!e.hasClass("expando")) { 161 | var content = e.attr("data_path").toLowerCase(); 162 | var match = searchMatch(content, query); 163 | if (match && !searchSet) { 164 | var url = dox.rootPath + "/" + e.attr("data_path").split(".").join("/") + ".html"; 165 | $("#searchForm").attr("action", url); 166 | searchSet = true; 167 | } 168 | e.css("display", match ? "" : "none"); 169 | } 170 | }); 171 | 172 | } 173 | 174 | function searchMatch(text, query) { 175 | var textParts = text.split("."); 176 | var queryParts = query.split("."); 177 | if (queryParts.length > textParts.length) { 178 | return false; 179 | } 180 | if (queryParts.length == 1) { 181 | return text.indexOf(query) > -1; 182 | } 183 | for (i = 0; i < queryParts.length; ++i) { 184 | if (textParts[i].indexOf(queryParts[i]) != 0) { // starts with 185 | return false; 186 | } 187 | } 188 | return true; 189 | } -------------------------------------------------------------------------------- /docs/nav.js: -------------------------------------------------------------------------------- 1 | var navContent=''; -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | body, html { height:100%; padding:0px !important; overflow:hidden; } 2 | .navbar { position:static !important; margin:0px !important;} 3 | #container { height:95%; } 4 | #nav { overflow-y:scroll; height:100%; float:left; width:250px; } 5 | #content { overflow-y:scroll; height:100%; padding:0px 12px; } 6 | /*#content .header { position:absolute; background:rgba(255,255,255,0.9); left:250px; right:15px; padding:0px 12px; border-bottom:1px solid #EEE;}*/ 7 | /*#content .body { padding-top:80px;}*/ 8 | 9 | .doc { margin-top:16px; } 10 | .toggle-hide { display:block; } 11 | .toggle-show { display:none; } 12 | .toggle-on .toggle-hide { display:none !important; } 13 | .toggle-on .toggle-show { display:block !important; } 14 | .related-types .toggle-hide { padding-top:4px; } 15 | .availability { margin-left:14px; color:#93a1a1; } 16 | .doc { margin-left:14px; } 17 | .viewsource { float:right; background:#f0f0f0; padding:4px 10px; border-radius: 4px; margin-top: 15px; } 18 | 19 | #nav li { list-style-type: none; } 20 | #nav .expando > ul { display:none; } 21 | #nav .expando.expanded > ul { display:block; } 22 | #nav ul, #nav li li { margin-left:14px; } 23 | #nav img { padding-right:4px; } 24 | #nav .pack { display:none; } 25 | 26 | #nav.searching .expando > ul { display:block; } 27 | #nav.searching .expando > div { display:none; } 28 | #nav.searching ul { margin-left:0px; } 29 | #nav.searching .pack { display:inline; } 30 | 31 | h1 code { padding:4px; font-size:15px; } 32 | h3 code { padding:4px; font-size:13px; } 33 | 34 | code { background:#FDF6E3; color:#002b36; tab-size:4; } /* light */ 35 | code a { text-decoration:none; } 36 | code a:hover { text-decoration:underline; } 37 | 38 | /*code.dark { background:#272822; color:#F8F8F2; border:none; } */ 39 | code .identifier { color:#002b36; } 40 | code .type { color:#268bd2; } 41 | code .keyword { color:#dc322f; } 42 | code .directive { color:#2aa198; } 43 | code .constant { color:#AE81FF; } 44 | code .comment { color:#75715E; } 45 | code .string { color:#E6DB74; } 46 | code .macro { color:#A6E22A; } 47 | code .inactive { color:#75715E; } 48 | 49 | .navbar-fixed-top { position: fixed; } 50 | .navbar-fixed-top .navbar-inner, 51 | .navbar-fixed-bottom .navbar-inner { padding: 0; } 52 | 53 | .inherited { 54 | display: none; 55 | } -------------------------------------------------------------------------------- /docs/triangle-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yupswing/akifox-transform/fc786ab784f69d735ba114497e9ff704d866ccc9/docs/triangle-closed.png -------------------------------------------------------------------------------- /docs/triangle-opened.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yupswing/akifox-transform/fc786ab784f69d735ba114497e9ff704d866ccc9/docs/triangle-opened.png -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "akifox-transform", 3 | "url" : "https://github.com/yupswing/akifox-transform", 4 | "license": "MIT", 5 | "tags": ["openfl","cross","flash","cpp","matrix","transform"], 6 | "description": "Affine matrix transformations with pivot point", 7 | "version": "2.2.1", 8 | "releasenote": "Better debugDraw", 9 | "contributors": ["yupswing"], 10 | "dependencies": {"openfl" : ""} 11 | } 12 | -------------------------------------------------------------------------------- /samples/interactive/README.md: -------------------------------------------------------------------------------- 1 | Please refer to the main README.md for information about this example -------------------------------------------------------------------------------- /samples/interactive/assets/fonts/04B_03__.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yupswing/akifox-transform/fc786ab784f69d735ba114497e9ff704d866ccc9/samples/interactive/assets/fonts/04B_03__.ttf -------------------------------------------------------------------------------- /samples/interactive/assets/graphics/graphics_here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yupswing/akifox-transform/fc786ab784f69d735ba114497e9ff704d866ccc9/samples/interactive/assets/graphics/graphics_here -------------------------------------------------------------------------------- /samples/interactive/assets/graphics/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yupswing/akifox-transform/fc786ab784f69d735ba114497e9ff704d866ccc9/samples/interactive/assets/graphics/test.png -------------------------------------------------------------------------------- /samples/interactive/assets/openfl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 58 | 61 | 64 | 68 | 74 | 75 | 79 | 85 | 86 | 87 | 97 | 99 | 105 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /samples/interactive/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/interactive/src/Main.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import openfl.Lib; 4 | import openfl.Assets; 5 | 6 | import openfl.geom.Point; 7 | 8 | import openfl.display.Bitmap; 9 | import openfl.display.BitmapData; 10 | import openfl.display.Sprite; 11 | 12 | import openfl.events.Event; 13 | import openfl.events.MouseEvent; 14 | import openfl.events.KeyboardEvent; 15 | 16 | import openfl.ui.Mouse; 17 | import openfl.ui.Keyboard; 18 | 19 | import motion.Actuate; 20 | 21 | import com.akifox.transform.Transformation; 22 | 23 | class Main extends Sprite { 24 | 25 | var lastPoint:Point; 26 | var debugdraw = true; 27 | var myspriteTrs:Transformation; 28 | 29 | // SETUP 30 | // ######################################################################### 31 | 32 | public function new () { 33 | 34 | super(); 35 | 36 | Mouse.hide(); 37 | 38 | var ox = 200; 39 | var oy = 200; 40 | var ow = 100; 41 | var oh = 100; 42 | 43 | ox = Std.int(Lib.current.stage.stageWidth/2-ow/2); 44 | oy = Std.int(Lib.current.stage.stageHeight/2-oh/2); 45 | 46 | // example with sprite 47 | /* var mysprite:Sprite = new Sprite(); 48 | mysprite.graphics.beginFill(0xFF0000,1); 49 | mysprite.graphics.drawRect(0,0,ow,oh); 50 | mysprite.graphics.endFill();*/ 51 | 52 | // example with bitmap 53 | var bitmapData = Assets.getBitmapData ("graphics/test.png"); 54 | var mysprite:Bitmap = new Bitmap(bitmapData); 55 | mysprite.smoothing = true; 56 | 57 | // set properties 58 | mysprite.x = ox; 59 | mysprite.y = oy; 60 | mysprite.alpha = 0.8; 61 | 62 | // create a transformation object bound to the sprite 63 | // the matrix identity set the transformation identity with a default translation to the center 64 | var matrixIdentity = new openfl.geom.Matrix(1,0,0,1,ox,oy); 65 | myspriteTrs = new Transformation(matrixIdentity); 66 | myspriteTrs.bind(mysprite); 67 | myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_MIDDLE_CENTER); 68 | lastPoint = myspriteTrs.getPivot(); 69 | 70 | 71 | // event listeners 72 | Lib.current.stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePointer); 73 | 74 | Lib.current.stage.addEventListener(MouseEvent.MOUSE_DOWN, stage_onMouseDown); 75 | 76 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); 77 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onSpecialKeyDown); 78 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onSpecialKeyUp); 79 | 80 | myspriteTrs.addEventListener(Transformation.TRANSFORM, onTransform); 81 | myspriteTrs.addEventListener(Transformation.PIVOT_CHANGE, onPivotChange); 82 | 83 | 84 | addChild(mysprite); 85 | addChild(myspriteTrs.spriteDebug); 86 | 87 | // first draw 88 | drawInterface(); 89 | myspriteTrs.debugDraw(); 90 | 91 | } 92 | 93 | 94 | // EVENTS FROM THE TRANSFORMATION CLASS 95 | // ######################################################################### 96 | 97 | public function onTransform(event:Event) { 98 | if (debugdraw) myspriteTrs.debugDraw(); 99 | } 100 | public function onPivotChange(event:Event) { 101 | if (debugdraw) myspriteTrs.debugDraw(); 102 | } 103 | 104 | 105 | 106 | // ######################################################################### 107 | // ######################################################################### 108 | // INTERACTIVE 109 | // ######################################################################### 110 | 111 | // general input vars 112 | var mousedownPoint:Point; //point on click 113 | var centerPoint:Point; //pivot point 114 | var dragged:Bool=false; 115 | 116 | // action flags 117 | var skewingMode = false; 118 | var scalingMode = false; 119 | var rotatingMode = false; 120 | var movingMode = false; 121 | 122 | // rotation vars 123 | var rotationAngle:Float=0; 124 | var lastRotationAngle:Float=0; 125 | 126 | // scale vars 127 | var distanceCenterMousedown:Float; 128 | var currentScale:Float; 129 | 130 | // isometrize vars 131 | var axisX:Point; 132 | var axisY:Point; 133 | 134 | 135 | private function stage_onMouseDown (event:MouseEvent):Void { 136 | dragged = false; 137 | centerPoint = myspriteTrs.getPivot(); 138 | mousedownPoint = new Point(Std.int(event.stageX),Std.int(event.stageY)); 139 | 140 | // scale setup 141 | currentScale = myspriteTrs.getScaleX(); // scale x and y will be the same 142 | distanceCenterMousedown = Transformation.distance(mousedownPoint,centerPoint); 143 | 144 | // rotation setup 145 | changeRotation(false); 146 | 147 | // isometrize setup 148 | axisX = new Point(1, 0); //vector for x - axis 149 | axisY = new Point(0, 1); //vector for y - axis 150 | 151 | // simulate a first movement to execute possible actions (modifiers key) 152 | stage_onMouseMove(event); 153 | 154 | // event listeners for dragging 155 | stage.addEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); 156 | stage.addEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); 157 | 158 | } 159 | 160 | 161 | private function stage_onMouseMove (event:MouseEvent):Void { 162 | if (!dragged && (event.shiftKey || event.altKey || event.ctrlKey)) dragged = true; 163 | if (!dragged && Transformation.distance(mousedownPoint,new Point(Std.int(event.stageX),Std.int(event.stageY))) > 5) dragged = true; 164 | if (dragged) { 165 | if (event.shiftKey || event.altKey || event.ctrlKey) { 166 | if (event.shiftKey) changeRotation(); 167 | if (event.altKey) changeScale(event); 168 | if (event.ctrlKey) changeSkew(event); 169 | } else { 170 | // * isometrize test 171 | //changeIsometrize(event); 172 | #if ios 173 | // * iphone test 174 | rotatingMode = true; 175 | scalingMode = true; 176 | changeRotation(); 177 | changeScale(event); 178 | #else 179 | // * normal test 180 | movingMode = true; 181 | changePosition(event); 182 | #end 183 | } 184 | } 185 | } 186 | 187 | private function stage_onMouseUp (event:MouseEvent):Void { 188 | if (!dragged) { 189 | // normal click 190 | changePivot(event); 191 | } 192 | dragged = false; 193 | 194 | skewingMode = event.ctrlKey; 195 | scalingMode = event.altKey; 196 | rotatingMode = event.shiftKey; 197 | movingMode = false; 198 | 199 | stage.removeEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); 200 | stage.removeEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); 201 | drawInterface(); 202 | 203 | } 204 | 205 | private function updatePointer (event:MouseEvent):Void { 206 | lastPoint = new Point(Std.int(event.stageX),Std.int(event.stageY)); 207 | drawInterface(); 208 | } 209 | 210 | 211 | // CHANGE FUNCTIONS (sprite transformations) 212 | // ######################################################################### 213 | 214 | private function changePivot (event:MouseEvent):Void { 215 | myspriteTrs.setPivot(new Point(Std.int(event.stageX),Std.int(event.stageY))); 216 | } 217 | 218 | private function changePosition(event:MouseEvent) { 219 | myspriteTrs.setTranslation(new Point(Std.int(event.stageX),Std.int(event.stageY))); 220 | } 221 | 222 | private function changeRotation(?rotate:Bool=true) { 223 | var pt:Point = myspriteTrs.getPivot(); 224 | var aa:Point = new Point(lastPoint.x,lastPoint.y); 225 | aa.x-=pt.x; 226 | aa.y-=pt.y; 227 | rotationAngle = Math.atan2(aa.x, aa.y); 228 | if (rotate) myspriteTrs.rotate(-(rotationAngle-lastRotationAngle)/Transformation.DEG2RAD); 229 | lastRotationAngle=rotationAngle; 230 | //trace('get',myspriteTrs.getRotation()); 231 | } 232 | 233 | private function changeScale(event:MouseEvent) { 234 | var dNowCenter = Transformation.distance(centerPoint,new Point(Std.int(event.stageX),Std.int(event.stageY))); 235 | myspriteTrs.setScale(dNowCenter/distanceCenterMousedown*currentScale); 236 | //trace('set/get',dNowCenter/distanceCenterMousedown*currentScale,myspriteTrs.getScaleX()); 237 | } 238 | 239 | private function changeSkew(event:MouseEvent) { 240 | var xs = (Std.int(event.stageX)-Lib.current.stage.stageWidth/2)/(Lib.current.stage.stageWidth/2); 241 | var ys = (Std.int(event.stageY)-Lib.current.stage.stageHeight/2)/(Lib.current.stage.stageHeight/2); 242 | myspriteTrs.setSkew(xs*50,ys*50); 243 | } 244 | 245 | private function changeIsometrize(event:MouseEvent) { 246 | // source: http://code.tutsplus.com/tutorials/understanding-affine-transformations-with-matrix-mathematics--active-10884 247 | var x:Float=event.stageX; 248 | var y:Float=event.stageY; 249 | var f1:Point = myspriteTrs.getPivot(); 250 | //f1.x>0 //front side 251 | axisX.setTo(x - f1.x, y - f1.y); //determine orientation (but magnitude changed as well) 252 | axisX.normalize(1); //fix magnitude of vector with new orientation to 1 unit 253 | myspriteTrs.setMatrixTo(axisX.x, axisX.y, axisY.x, axisY.y, 200, 200); 254 | } 255 | 256 | 257 | // INTERFACE 258 | // ######################################################################### 259 | 260 | private function drawInterface(){ 261 | var pt:Point = myspriteTrs.getPivot(); 262 | 263 | graphics.clear(); 264 | 265 | //rotation point 266 | graphics.beginFill(((dragged)?0xFF0000:0x0000FF),1); 267 | graphics.drawCircle(lastPoint.x,lastPoint.y,5); 268 | graphics.endFill(); 269 | 270 | if (rotatingMode || scalingMode) { 271 | graphics.lineStyle(2, 0x00FF00, .5, false); 272 | graphics.moveTo(pt.x,pt.y); 273 | graphics.lineTo(lastPoint.x,lastPoint.y); 274 | } 275 | 276 | if (skewingMode) { 277 | graphics.lineStyle(2, 0x0000FF, .5, false); 278 | graphics.moveTo(Std.int(Lib.current.stage.stageWidth/2),0); 279 | graphics.lineTo(Std.int(Lib.current.stage.stageWidth/2),Std.int(Lib.current.stage.stageHeight)); 280 | graphics.moveTo(0,Std.int(Lib.current.stage.stageHeight/2)); 281 | graphics.lineTo(Std.int(Lib.current.stage.stageWidth),Std.int(Lib.current.stage.stageHeight/2)); 282 | } 283 | 284 | } 285 | 286 | 287 | // INPUT 288 | // ######################################################################### 289 | 290 | private function onSpecialKeyDown(event:KeyboardEvent):Void { 291 | 292 | switch (event.keyCode) { 293 | case Keyboard.SHIFT: rotatingMode = true; 294 | case Keyboard.CONTROL: skewingMode = true; 295 | case Keyboard.ALTERNATE: scalingMode = true; 296 | } 297 | drawInterface(); 298 | } 299 | 300 | private function onSpecialKeyUp(event:KeyboardEvent):Void { 301 | 302 | switch (event.keyCode) { 303 | case Keyboard.SHIFT: rotatingMode = false; 304 | case Keyboard.CONTROL: skewingMode = false; 305 | case Keyboard.ALTERNATE: scalingMode = false; 306 | } 307 | drawInterface(); 308 | } 309 | 310 | private function onKeyUp(event:KeyboardEvent):Void { 311 | if (dragged) return; 312 | 313 | switch (event.keyCode) { 314 | // Z for debugging 315 | case Keyboard.Z: debugToggle(); 316 | // space to reset 317 | case Keyboard.SPACE: myspriteTrs.identity(); 318 | // arrows to move 319 | case Keyboard.UP: myspriteTrs.translateY(-15); 320 | case Keyboard.DOWN: myspriteTrs.translateY(15); 321 | case Keyboard.LEFT: myspriteTrs.translateX(-15); 322 | case Keyboard.RIGHT: myspriteTrs.translateX(15); 323 | // Q A W S to skew 324 | case Keyboard.Q: myspriteTrs.skewX(15); 325 | case Keyboard.A: myspriteTrs.skewX(-15); 326 | case Keyboard.W: myspriteTrs.skewY(15); 327 | case Keyboard.S: myspriteTrs.skewY(-15); 328 | // E D to skew 329 | case Keyboard.E: myspriteTrs.scale(1.5); 330 | case Keyboard.D: myspriteTrs.scale(1/1.5); 331 | // R F to rotate 332 | case Keyboard.R: myspriteTrs.rotate(-15); 333 | case Keyboard.F: myspriteTrs.rotate(15); 334 | // T G to translate with actuate 335 | case Keyboard.T: Actuate.tween (myspriteTrs, 1, { translationX: 100, translationY:100 } ); 336 | case Keyboard.G: Actuate.tween (myspriteTrs, 1, { translationX: 200, translationY:200 } ); 337 | // pivot anchored point 338 | case Keyboard.NUMBER_1: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_TOP_LEFT); 339 | case Keyboard.NUMBER_2: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_TOP_CENTER); 340 | case Keyboard.NUMBER_3: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_TOP_RIGHT); 341 | case Keyboard.NUMBER_4: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_MIDDLE_LEFT); 342 | case Keyboard.NUMBER_5: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_MIDDLE_CENTER); 343 | case Keyboard.NUMBER_6: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_MIDDLE_RIGHT); 344 | case Keyboard.NUMBER_7: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_BOTTOM_LEFT); 345 | case Keyboard.NUMBER_8: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_BOTTOM_CENTER); 346 | case Keyboard.NUMBER_9: myspriteTrs.setAnchoredPivot(Transformation.ANCHOR_BOTTOM_RIGHT); 347 | } 348 | } 349 | 350 | private function debugToggle(){ 351 | if (debugdraw) myspriteTrs.debugClear(); 352 | else myspriteTrs.debugDraw(); 353 | debugdraw = !debugdraw; 354 | } 355 | 356 | } 357 | -------------------------------------------------------------------------------- /scripts/gen_docs.hxml: -------------------------------------------------------------------------------- 1 | -lib openfl 2 | -lib lime 3 | --no-output 4 | --macro include('com.akifox.transform') 5 | -D doc-gen 6 | 7 | --each 8 | 9 | -neko all.n 10 | -xml bin/xml/neko.xml 11 | # --next 12 | 13 | #-swf all.swf 14 | #-swf-version 8 15 | #-xml bin/xml/flash8.xml 16 | #-D flash_lite 17 | #--next 18 | 19 | # -js all.js 20 | # -xml bin/xml/js.xml 21 | # --next 22 | 23 | # -swf all9.swf 24 | # -xml bin/xml/flash.xml 25 | # -swf-version 11.4 26 | # --next 27 | 28 | # -php all_php 29 | # -xml bin/xml/php.xml 30 | #--next 31 | 32 | # -cpp all_cpp 33 | # -xml bin/xml/cpp.xml 34 | # -D xmldoc 35 | # -D HXCPP_MULTI_THREADED 36 | #--next 37 | 38 | # -java all_java 39 | # -xml bin/xml/java.xml 40 | # -D xmldoc 41 | # --next 42 | 43 | # -cs all_cs 44 | # -D unsafe 45 | # -xml bin/xml/cs.xml 46 | # -D xmldoc 47 | # --next 48 | 49 | # -python all_py 50 | # -xml bin/xml/python.xml 51 | # -D xmldoc 52 | # --next 53 | 54 | # --interp 55 | # -xml bin/xml/cross.xml -------------------------------------------------------------------------------- /scripts/gen_docs.sh: -------------------------------------------------------------------------------- 1 | rm -rf docs/* && 2 | rm -rf docs/* docs/.??* && 3 | rm -rf bin/xml/* bin/xml/.??* && 4 | haxe scripts/gen_docs.hxml && 5 | haxelib run dox -o docs -i bin/xml -in com && 6 | open docs/index.html -------------------------------------------------------------------------------- /scripts/make_haxelib.sh: -------------------------------------------------------------------------------- 1 | rm -rf bin/haxelib-akifox-transform.zip && 2 | rm -rf samples/interactive/bin && 3 | zip bin/haxelib-akifox-transform.zip -r samples com docs README.md LICENSE haxelib.json; --------------------------------------------------------------------------------