PageRenderTime 76ms CodeModel.GetById 39ms RepoModel.GetById 1ms app.codeStats 0ms

/nickro/greensock/transform/TransformItem.as

http://my-project-nickro.googlecode.com/
ActionScript | 1079 lines | 776 code | 110 blank | 193 comment | 229 complexity | 318eabdcff21a95da2d77967730788a8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. * VERSION: 1.87
  3. * DATE: 2010-01-31
  4. * AS3
  5. * UPDATES AND DOCUMENTATION AT: http://blog.greensock.com/transformmanageras3/
  6. **/
  7. package org.nickro.greensock.transform {
  8. import org.nickro.greensock.events.TransformEvent;
  9. import org.nickro.greensock.transform.utils.MatrixTools;
  10. import flash.display.DisplayObject;
  11. import flash.display.Graphics;
  12. import flash.display.InteractiveObject;
  13. import flash.display.Sprite;
  14. import flash.display.Stage;
  15. import flash.events.Event;
  16. import flash.events.EventDispatcher;
  17. import flash.events.MouseEvent;
  18. import flash.geom.Matrix;
  19. import flash.geom.Point;
  20. import flash.geom.Rectangle;
  21. import flash.text.TextField;
  22. import flash.text.TextFormat;
  23. import flash.text.TextLineMetrics;
  24. import flash.utils.getDefinitionByName;
  25. /**
  26. * TransformManager automatically creates a TransformItem instance for each DisplayObject
  27. * that it manages, using it to apply various transformations and check constraints. Typically
  28. * you won't need to interact much with the TranformItem instances except if you need to
  29. * apply item-specific properties like minScaleX, maxScaleX, minScaleY, maxScaleY, or if you
  30. * need to apply transformations directly. You can use TransformManager's <code>getItem()</code>
  31. * method to get the TransformItem instance associated with a particular DisplayObject anytime
  32. * after it is added to the TransformManager instance, like:<br /><br /><code>
  33. *
  34. * var myItem:TransformItem = myManager.getItem(myObject);<br /><br /></code>
  35. *
  36. * <b>Copyright 2010, GreenSock. All rights reserved.</b> This work is subject to the terms in <a href="http://www.greensock.com/eula.html">http://www.greensock.com/eula.html</a> or for corporate Club GreenSock members, the software agreement that was issued with the corporate membership.
  37. *
  38. * @author Jack Doyle, jack@greensock.com
  39. */
  40. public class TransformItem extends EventDispatcher {
  41. public static const VERSION:Number = 1.87;
  42. /** @private precomputation for speed**/
  43. protected static const _DEG2RAD:Number = Math.PI / 180;
  44. /** @private precomputation for speed**/
  45. protected static const _RAD2DEG:Number = 180 / Math.PI;
  46. /** @private **/
  47. protected static var _proxyCount:uint = 0;
  48. /** @private **/
  49. protected var _hasSelectableText:Boolean;
  50. /** @private **/
  51. protected var _stage:Stage;
  52. /** @private **/
  53. protected var _scaleMode:String;
  54. /** @private if scaleMode is normal. this will point to the _targetObject. Otherwise, we create a proxy that we scale normally and then use it to scale the width/height or setSize() the targetObject.**/
  55. protected var _target:DisplayObject;
  56. /** @private **/
  57. protected var _proxy:InteractiveObject;
  58. /** @private used for TextFields - for some odd reason, TextFields created in the IDE must be offset 2 pixels left and up in order to line up properly with their scaled counterparts. **/
  59. protected var _offset:Point;
  60. /** @private **/
  61. protected var _origin:Point;
  62. /** @private **/
  63. protected var _localOrigin:Point;
  64. /** @private **/
  65. protected var _baseRect:Rectangle;
  66. /** @private **/
  67. protected var _bounds:Rectangle;
  68. /** @private **/
  69. protected var _targetObject:DisplayObject;
  70. /** @private **/
  71. protected var _allowDelete:Boolean;
  72. /** @private **/
  73. protected var _constrainScale:Boolean;
  74. /** @private **/
  75. protected var _lockScale:Boolean;
  76. /** @private **/
  77. protected var _lockRotation:Boolean;
  78. /** @private **/
  79. protected var _lockPosition:Boolean;
  80. /** @private **/
  81. protected var _enabled:Boolean;
  82. /** @private **/
  83. protected var _selected:Boolean;
  84. /** @private **/
  85. protected var _minScaleX:Number;
  86. /** @private **/
  87. protected var _minScaleY:Number;
  88. /** @private **/
  89. protected var _maxScaleX:Number;
  90. /** @private **/
  91. protected var _maxScaleY:Number;
  92. /** @private **/
  93. protected var _cornerAngleTL:Number;
  94. /** @private **/
  95. protected var _cornerAngleTR:Number;
  96. /** @private **/
  97. protected var _cornerAngleBR:Number;
  98. /** @private **/
  99. protected var _cornerAngleBL:Number;
  100. /** @private **/
  101. protected var _createdManager:TransformManager;
  102. /** @private **/
  103. protected var _isFlex:Boolean;
  104. /** @private **/
  105. protected var _frameCount:uint = 0;
  106. /** @private **/
  107. protected var _dispatchScaleEvents:Boolean;
  108. /** @private **/
  109. protected var _dispatchMoveEvents:Boolean;
  110. /** @private **/
  111. protected var _dispatchRotateEvents:Boolean;
  112. /**
  113. * Constructor
  114. *
  115. * @param $targetObject DisplayObject to be managed
  116. * @param $vars An object specifying any properties that should be set upon instantiation, like <code>{constrainScale:true, lockRotation:true, bounds:new Rectangle(0, 0, 500, 300)}</code>.
  117. */
  118. public function TransformItem($targetObject:DisplayObject, $vars:Object) {
  119. if (TransformManager.VERSION < 1.87) {
  120. trace("TransformManager Error: You have an outdated TransformManager-related class file. You may need to clear your ASO files. Please make sure you're using the latest version of TransformManager, TransformItem, and TransformItemTF, available from www.greensock.com.");
  121. }
  122. init($targetObject, $vars);
  123. }
  124. /** @private **/
  125. protected function init($targetObject:DisplayObject, $vars:Object):void {
  126. try {
  127. _isFlex = Boolean(getDefinitionByName("mx.managers.SystemManager")); // SystemManager is the first display class created within a Flex application
  128. } catch ($e:Error) {
  129. _isFlex = false;
  130. }
  131. _targetObject = $targetObject;
  132. _baseRect = _targetObject.getBounds(_targetObject);
  133. _allowDelete = setDefault($vars.allowDelete, false);
  134. _constrainScale = setDefault($vars.constrainScale, false);
  135. _lockScale = setDefault($vars.lockScale, false);
  136. _lockRotation = setDefault($vars.lockRotation, false);
  137. _lockPosition = setDefault($vars.lockPosition, false);
  138. _hasSelectableText = setDefault($vars.hasSelectableText, (_targetObject is TextField) ? true : false);
  139. this.scaleMode = setDefault($vars.scaleMode, (_hasSelectableText) ? TransformManager.SCALE_WIDTH_AND_HEIGHT : TransformManager.SCALE_NORMAL);
  140. this.minScaleX = setDefault($vars.minScaleX, -Infinity);
  141. this.minScaleY = setDefault($vars.minScaleY, -Infinity);
  142. this.maxScaleX = setDefault($vars.maxScaleX, Infinity);
  143. this.maxScaleY = setDefault($vars.maxScaleY, Infinity);
  144. _bounds = $vars.bounds;
  145. this.origin = new Point(_targetObject.x, _targetObject.y);
  146. if ($vars.manager == undefined) {
  147. $vars.items = [this];
  148. _createdManager = new TransformManager($vars);
  149. }
  150. if (_targetObject.stage != null) {
  151. _stage = _targetObject.stage;
  152. } else {
  153. _targetObject.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
  154. }
  155. _selected = false;
  156. _enabled = !Boolean($vars.enabled);
  157. this.enabled = !_enabled;
  158. }
  159. /** @private **/
  160. protected function onAddedToStage($e:Event):void { //needed to keep track of _stage primarily for the MOUSE_UP listening and for the scenario when a _targetObject is removed from the stage immediately when selected (very rare and somewhat unintuitive scenario, but a user did want to do it)
  161. _stage = _targetObject.stage;
  162. try {
  163. _isFlex = Boolean(getDefinitionByName("mx.managers.SystemManager")); // SystemManager is the first display class created within a Flex application
  164. } catch ($e:Error) {
  165. _isFlex = false;
  166. }
  167. if (_proxy != null) {
  168. _targetObject.parent.addChild(_proxy);
  169. }
  170. _targetObject.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
  171. }
  172. //---- SELECTION ---------------------------------------------------------------------------------------
  173. /** @private **/
  174. protected function onMouseDown($e:MouseEvent):void {
  175. if (_hasSelectableText) {
  176. dispatchEvent(new TransformEvent(TransformEvent.MOUSE_DOWN, [this]));
  177. } else {
  178. _stage = _targetObject.stage; //must set this each time in case the stage changes (which can happen in an AIR app)
  179. _stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  180. dispatchEvent(new TransformEvent(TransformEvent.MOUSE_DOWN, [this], $e));
  181. if (_selected) {
  182. dispatchEvent(new TransformEvent(TransformEvent.SELECT_MOUSE_DOWN, [this], $e));
  183. }
  184. }
  185. }
  186. /** @private **/
  187. protected function onMouseUp($e:MouseEvent):void {
  188. _stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  189. if (!_hasSelectableText && _selected) {
  190. dispatchEvent(new TransformEvent(TransformEvent.SELECT_MOUSE_UP, [this], $e));
  191. }
  192. }
  193. /** @private **/
  194. protected function onRollOverItem($e:MouseEvent):void {
  195. if (_selected) {
  196. dispatchEvent(new TransformEvent(TransformEvent.ROLL_OVER_SELECTED, [this], $e));
  197. }
  198. }
  199. /** @private **/
  200. protected function onRollOutItem($e:MouseEvent):void {
  201. if (_selected) {
  202. dispatchEvent(new TransformEvent(TransformEvent.ROLL_OUT_SELECTED, [this], $e));
  203. }
  204. }
  205. //---- GENERAL -----------------------------------------------------------------------------------------
  206. /** @private **/
  207. public function update($e:Event = null):void {
  208. _baseRect = _targetObject.getBounds(_targetObject);
  209. setCornerAngles();
  210. if (_proxy != null) {
  211. calibrateProxy();
  212. }
  213. dispatchEvent(new TransformEvent(TransformEvent.UPDATE, [this]));
  214. }
  215. /**
  216. * Allows listening for the following events:
  217. * <ul>
  218. * <li> TransformEvent.MOVE
  219. * <li> TransformEvent.SCALE
  220. * <li> TransformEvent.ROTATE
  221. * <li> TransformEvent.SELECT
  222. * <li> TransformEvent.DELETE
  223. * <li> TransformEvent.UPDATE
  224. * </ul>
  225. *
  226. * @param $type Event type
  227. * @param $listener Listener function
  228. * @param $useCapture Use capture phase
  229. * @param $priority Priority
  230. * @param $useWeakReference Use weak reference
  231. */
  232. override public function addEventListener($type:String, $listener:Function, $useCapture:Boolean=false, $priority:int=0, $useWeakReference:Boolean=false):void {
  233. //to improve performance, only dispatch the continuous move/scale/rotate events when necessary (the ones that fire on MOUSE_MOVE).
  234. if ($type == TransformEvent.MOVE) {
  235. _dispatchMoveEvents = true;
  236. } else if ($type == TransformEvent.SCALE) {
  237. _dispatchScaleEvents = true;
  238. } else if ($type == TransformEvent.ROTATE) {
  239. _dispatchRotateEvents = true;
  240. }
  241. super.addEventListener($type, $listener, $useCapture, $priority, $useWeakReference);
  242. }
  243. /** @private In Flex, we cannot addChild() immediately because the container needs time to instantiate or it will throw errors, so we wait 3 frames...**/
  244. protected function autoCalibrateProxy($e:Event=null):void {
  245. if (_frameCount >= 3) {
  246. _targetObject.removeEventListener(Event.ENTER_FRAME, autoCalibrateProxy);
  247. if (_targetObject.parent) {
  248. _targetObject.parent.addChild(_proxy);
  249. }
  250. _target = _proxy;
  251. calibrateProxy();
  252. _frameCount = 0;
  253. } else {
  254. _frameCount++;
  255. }
  256. }
  257. /** @private **/
  258. protected function createProxy():void {
  259. removeProxy();
  260. if (_hasSelectableText) {
  261. _proxy = (_isFlex) ? new (getDefinitionByName("mx.core.UITextField"))() : new TextField();
  262. } else {
  263. _proxy = (_isFlex) ? new (getDefinitionByName("mx.core.UIComponent"))() : new Sprite();
  264. }
  265. _proxyCount++;
  266. _proxy.name = "__tmProxy" + _proxyCount; //important: FlexTransformManager looks for this name in order to avoid infinite loop problems with addChild()
  267. _proxy.visible = false;
  268. try {
  269. _target = _proxy;
  270. _targetObject.parent.addChild(_proxy);
  271. } catch ($e:Error) {
  272. _target = _targetObject;
  273. _targetObject.addEventListener(Event.ENTER_FRAME, autoCalibrateProxy); //In Flex, sometimes the parent can need a few frames to instantiate properly
  274. }
  275. _offset = new Point(0, 0);
  276. //TextFields created in the IDE have a different gutter than ones created via ActionScript (2 pixels), so we must attempt to discern between the 2 using the line metrics...
  277. if (_targetObject is TextField) {
  278. var tf:TextField = (_targetObject as TextField);
  279. var isEmpty:Boolean = false;
  280. if (tf.text == "") {
  281. tf.text = "Y"; //temporarily dump a character in for measurement.
  282. isEmpty = true;
  283. }
  284. var format:TextFormat = tf.getTextFormat(0, 1);
  285. var altFormat:TextFormat = tf.getTextFormat(0, 1);
  286. altFormat.align = "left";
  287. tf.setTextFormat(altFormat, 0, 1);
  288. var metrics:TextLineMetrics = tf.getLineMetrics(0);
  289. if (metrics.x == 0) {
  290. _offset = new Point(-2, -2);
  291. }
  292. tf.setTextFormat(format, 0, 1);
  293. if (isEmpty) {
  294. tf.text = "";
  295. }
  296. }
  297. calibrateProxy();
  298. }
  299. /** @private **/
  300. protected function removeProxy():void {
  301. if (_proxy != null) {
  302. if (_proxy.parent != null) {
  303. _proxy.parent.removeChild(_proxy);
  304. }
  305. _proxy = null;
  306. }
  307. _target = _targetObject;
  308. }
  309. /** @private **/
  310. protected function calibrateProxy():void {
  311. var m:Matrix = _targetObject.transform.matrix;
  312. _targetObject.transform.matrix = new Matrix(); //to clear all transformations
  313. if (!_hasSelectableText) {
  314. var r:Rectangle = _targetObject.getBounds(_targetObject);
  315. var g:Graphics = (_proxy as Sprite).graphics;
  316. g.clear();
  317. g.beginFill(0xFF0000, 0);
  318. g.drawRect(r.x, r.y, _targetObject.width, _targetObject.height); //don't use r.width and r.height because often times Flex components report those inaccurately with getBounds()!
  319. g.endFill();
  320. }
  321. _proxy.width = _baseRect.width = _targetObject.width;
  322. _proxy.height = _baseRect.height = _targetObject.height;
  323. _proxy.transform.matrix = _targetObject.transform.matrix = m;
  324. }
  325. /** @private **/
  326. protected function setCornerAngles():void { //figures out the angles from the origin to each of the corners of the _bounds rectangle.
  327. if (_bounds != null) {
  328. _cornerAngleTL = TransformManager.positiveAngle(Math.atan2(_bounds.y - _origin.y, _bounds.x - _origin.x));
  329. _cornerAngleTR = TransformManager.positiveAngle(Math.atan2(_bounds.y - _origin.y, _bounds.right - _origin.x));
  330. _cornerAngleBR = TransformManager.positiveAngle(Math.atan2(_bounds.bottom - _origin.y, _bounds.right - _origin.x));
  331. _cornerAngleBL = TransformManager.positiveAngle(Math.atan2(_bounds.bottom - _origin.y, _bounds.x - _origin.x));
  332. }
  333. }
  334. /** @private **/
  335. protected function reposition():void { //Ensures that the _origin lines up with the _localOrigin.
  336. var p:Point = _target.parent.globalToLocal(_target.localToGlobal(_localOrigin));
  337. _target.x += _origin.x - p.x;
  338. _target.y += _origin.y - p.y;
  339. }
  340. /** @private **/
  341. public function onPressDelete($e:Event = null, $allowSelectableTextDelete:Boolean = false):Boolean {
  342. if (_enabled && _allowDelete && (_hasSelectableText == false || $allowSelectableTextDelete)) { //_hasSelectableText typically means it's a TextField in which case users should be able to hit the DELETE key without deleting the whole TextField.
  343. deleteObject();
  344. return true;
  345. }
  346. return false;
  347. }
  348. /** Deletes the DisplayObject (removing it from the display list) **/
  349. public function deleteObject():void {
  350. this.selected = false;
  351. if (_targetObject.parent) {
  352. _targetObject.parent.removeChild(_targetObject);
  353. }
  354. removeProxy();
  355. dispatchEvent(new TransformEvent(TransformEvent.DELETE, [this]));
  356. }
  357. /** @private **/
  358. public function forceEventDispatch($type:String):void {
  359. dispatchEvent(new TransformEvent($type, [this]));
  360. }
  361. /** Destroys the TransformItem instance, preparing for garbage collection **/
  362. public function destroy():void {
  363. this.enabled = false; //kills listeners too
  364. this.selected = false; //kills listeners too
  365. dispatchEvent(new TransformEvent(TransformEvent.DESTROY, [this]));
  366. }
  367. //---- MOVE --------------------------------------------------------------------------------------------
  368. /**
  369. * Moves the selected items by a certain number of pixels on the x axis and y axis
  370. *
  371. * @param $x Number of pixels to move the item along the x-axis (can be negative or positive)
  372. * @param $y Number of pixels to move the item along the y-axis (can be negative or positive)
  373. * @param $checkBounds If false, bounds will be ignored
  374. * @param $dispatchEvent If false, no MOVE events will be dispatched
  375. */
  376. public function move($x:Number, $y:Number, $checkBounds:Boolean = true, $dispatchEvent:Boolean = true):void {
  377. if (!_lockPosition) {
  378. if ($checkBounds && _bounds != null) {
  379. var safe:Object = {x:$x, y:$y};
  380. moveCheck($x, $y, safe);
  381. $x = safe.x;
  382. $y = safe.y;
  383. }
  384. _target.x += $x;
  385. _target.y += $y;
  386. _origin.x += $x;
  387. _origin.y += $y;
  388. if (_target != _targetObject) {
  389. _targetObject.x += $x;
  390. _targetObject.y += $y;
  391. }
  392. if ($dispatchEvent && _dispatchMoveEvents && ($x != 0 || $y != 0)) {
  393. dispatchEvent(new TransformEvent(TransformEvent.MOVE, [this]));
  394. }
  395. }
  396. }
  397. /** @private **/
  398. public function moveCheck($x:Number, $y:Number, $safe:Object):void { //Just checks to see if the translation will hit the bounds and edits the $safe object properties to make sure it doesn't
  399. if (_lockPosition) {
  400. $safe.x = $safe.y = 0;
  401. } else if (_bounds != null) {
  402. var r:Rectangle = _targetObject.getBounds(_targetObject.parent);
  403. r.offset($x, $y);
  404. if (!_bounds.containsRect(r)) {
  405. if (_bounds.right < r.right) {
  406. $x += _bounds.right - r.right;
  407. $safe.x = int(Math.min($safe.x, $x));
  408. } else if (_bounds.left > r.left) {
  409. $x += _bounds.left - r.left;
  410. $safe.x = int(Math.max($safe.x, $x));
  411. }
  412. if (_bounds.top > r.top) {
  413. $y += _bounds.top - r.top;
  414. $safe.y = int(Math.max($safe.y, $y));
  415. } else if (_bounds.bottom < r.bottom) {
  416. $y += _bounds.bottom - r.bottom;
  417. $safe.y = int(Math.min($safe.y, $y));
  418. }
  419. }
  420. }
  421. }
  422. //---- SCALE -------------------------------------------------------------------------------------------
  423. /**
  424. * Scales the item along the x- and y-axis using multipliers. Keep in mind that these are
  425. * not absolute values, so if the item's scaleX is 2 and you scale(2, 1), its new
  426. * scaleX would be 4 because 2 * 2 = 4.
  427. *
  428. * @param $sx Multiplier for scaling along the selection box's x-axis (which may or may not be the same as the selected item's y-axis, depending on whether or not multiple items are selected and if any are rotated)
  429. * @param $sy Multiplier for scaling along the selection box's y-axis (which may or may not be the same as the selected item's y-axis, depending on whether or not multiple items are selected and if any are rotated)
  430. * @param $angle Angle at which the item should be scaled (in Radians)
  431. * @param $checkBounds If false, bounds will be ignored
  432. * @param $dispatchEvent If false, no SCALE event will be dispatched
  433. *
  434. */
  435. public function scale($sx:Number, $sy:Number, $angle:Number = 0, $checkBounds:Boolean = true, $dispatchEvent:Boolean = true):void {
  436. if (!_lockScale) {
  437. scaleRotated($sx, $sy, $angle, -$angle, $checkBounds, $dispatchEvent);
  438. }
  439. }
  440. /** @private **/
  441. public function scaleRotated($sx:Number, $sy:Number, $angle:Number, $skew:Number, $checkBounds:Boolean = true, $dispatchEvent:Boolean = true):void {
  442. if (!_lockScale) {
  443. var m:Matrix = _target.transform.matrix;
  444. if ($angle != -$skew && Math.abs(($angle + $skew) % (Math.PI - 0.01)) < 0.01) { //protects against rounding errors in tiny decimals
  445. $skew = -$angle;
  446. }
  447. if ($checkBounds && _bounds != null) {
  448. var safe:Object = {sx:$sx, sy:$sy};
  449. scaleCheck(safe, $angle, $skew);
  450. $sx = safe.sx;
  451. $sy = safe.sy;
  452. }
  453. MatrixTools.scaleMatrix(m, $sx, $sy, $angle, $skew);
  454. if (_scaleMode == "scaleNormal") {
  455. _targetObject.transform.matrix = m;
  456. reposition();
  457. } else {
  458. _proxy.transform.matrix = m;
  459. reposition();
  460. var w:Number = Math.sqrt(m.a * m.a + m.b * m.b) * _baseRect.width;
  461. var h:Number = Math.sqrt(m.d * m.d + m.c * m.c) * _baseRect.height;
  462. var p:Point = _targetObject.parent.globalToLocal(_proxy.localToGlobal(_offset)); //had to use _targetObject.parent instead of _proxy.parent because of another bug in Flex that prevented _proxy items from accurately reporting their parent for a few frames after being added to the display list! Since they both have the same parent, this shouldn't matter though.
  463. //if (_scaleMode == "scaleWidthAndHeight") {
  464. _targetObject.width = w;
  465. _targetObject.height = h;
  466. //} else {
  467. // (_targetObject as Object).setSize((w % 1 > 0.5) ? int(w) + 1 : int(w), (h % 1 > 0.5) ? int(h) + 1 : int(h));
  468. //}
  469. _targetObject.rotation = _proxy.rotation;
  470. _targetObject.x = p.x;
  471. _targetObject.y = p.y;
  472. }
  473. if ($dispatchEvent && _dispatchScaleEvents && ($sx != 1 || $sy != 1)) {
  474. dispatchEvent(new TransformEvent(TransformEvent.SCALE, [this]));
  475. }
  476. }
  477. }
  478. /** @private **/
  479. public function scaleCheck($safe:Object, $angle:Number, $skew:Number):void { //Just checks to see if the scale will hit the bounds and edits the $safe object properties to make sure it doesn't
  480. if (_lockScale) {
  481. $safe.sx = $safe.sy = 1;
  482. } else {
  483. var sx:Number, sy:Number;
  484. var original:Matrix = _target.transform.matrix;
  485. var originalScaleX:Number = MatrixTools.getScaleX(original);
  486. var originalScaleY:Number = MatrixTools.getScaleY(original);
  487. var originalAngle:Number = MatrixTools.getAngle(original);
  488. var m:Matrix = original.clone();
  489. MatrixTools.scaleMatrix(m, $safe.sx, $safe.sy, $angle, $skew);
  490. if (this.hasScaleLimits) {
  491. var angleDif:Number = $angle - originalAngle;
  492. var skewDif:Number = $skew - MatrixTools.getSkew(original);
  493. if (angleDif == 0 && skewDif < 0.0001 && skewDif > -0.0001) {
  494. sx = MatrixTools.getScaleX(m);
  495. sy = MatrixTools.getScaleY(m);
  496. if (Math.abs(originalAngle - MatrixTools.getAngle(m)) > Math.PI * 0.51) { //flipped in both directions
  497. sx = -sx;
  498. sy = -sy;
  499. }
  500. var ratio:Number = originalScaleX / originalScaleY;
  501. if (sx > _maxScaleX) {
  502. $safe.sx = _maxScaleX / originalScaleX;
  503. if (_constrainScale) {
  504. $safe.sy = sy = $safe.sx / ratio;
  505. if ($safe.sx * $safe.sy < 0) { //make sure sy is negative if sx is.
  506. $safe.sy = sy = -sy;
  507. }
  508. }
  509. } else if (sx < _minScaleX) {
  510. $safe.sx = _minScaleX / originalScaleX;
  511. if (_constrainScale) {
  512. $safe.sy = sy = $safe.sx / ratio;
  513. if ($safe.sx * $safe.sy < 0) { //make sure sy is negative if sx is.
  514. $safe.sy = sy = -sy;
  515. }
  516. }
  517. }
  518. if (sy > _maxScaleY) {
  519. $safe.sy = _maxScaleY / originalScaleY;
  520. if (_constrainScale) {
  521. $safe.sx = $safe.sy * ratio;
  522. if ($safe.sx * $safe.sy < 0) { //make sure sx is negative if sy is.
  523. $safe.sx *= -1;
  524. }
  525. }
  526. } else if (sy < _minScaleY) {
  527. $safe.sy = _minScaleY / originalScaleY;
  528. if (_constrainScale) {
  529. $safe.sx = $safe.sy * ratio;
  530. if ($safe.sx * $safe.sy < 0) { //make sure sx is negative if sy is.
  531. $safe.sx *= -1;
  532. }
  533. }
  534. }
  535. m = original.clone();
  536. MatrixTools.scaleMatrix(m, $safe.sx, $safe.sy, $angle, $skew);
  537. } else {
  538. sx = MatrixTools.getScaleX(m);
  539. sy = MatrixTools.getScaleY(m);
  540. if (sx > _maxScaleX || sx < _minScaleX || sy > _maxScaleY || sy < _minScaleY) {
  541. $safe.sx = $safe.sy = 1;
  542. return;
  543. }
  544. }
  545. }
  546. _target.transform.matrix = m;
  547. reposition();
  548. if (_bounds != null) {
  549. if (!_bounds.containsRect(_target.getBounds(_target.parent))) {
  550. if ($safe.sy == 1) {
  551. _target.transform.matrix = original;
  552. iterateStretchX($safe, $angle, $skew);
  553. } else if ($safe.sx == 1) {
  554. _target.transform.matrix = original;
  555. iterateStretchY($safe, $angle, $skew);
  556. } else {
  557. /* potential future replacement technique - needs refinement for when there are skewed items near the edge
  558. var scaledBounds:Rectangle = _target.getBounds(_target.parent);
  559. sx = sy = 1;
  560. if (scaledBounds.right > _bounds.right && Math.abs(_bounds.right - _origin.x) > 0.5) {
  561. sx = (_bounds.right - _origin.x) / (scaledBounds.right - _origin.x);
  562. }
  563. if (scaledBounds.left < _bounds.left && Math.abs(_origin.x - _bounds.left) > 0.5) {
  564. sx = Math.min(sx, (_origin.x - _bounds.left) / (_origin.x - scaledBounds.left));
  565. }
  566. if (scaledBounds.bottom > _bounds.bottom && Math.abs(_bounds.bottom - _origin.y) > 0.5) {
  567. sy = (_bounds.bottom - _origin.y) / (scaledBounds.bottom - _origin.y);
  568. }
  569. if (scaledBounds.top < _bounds.top && Math.abs(_origin.y - _bounds.top) > 0.5) {
  570. sy = Math.min(sy, (_origin.y - _bounds.top) / (_origin.y - scaledBounds.top));
  571. }
  572. var s:Number = Math.min(sx, sy);
  573. $safe.sx *= s;
  574. $safe.sy *= s;
  575. */
  576. var i:int, corner:Point, cornerAngle:Number, oldLength:Number, newLength:Number, dx:Number, dy:Number;
  577. var minScale:Number = 1;
  578. var r:Rectangle = _target.getBounds(_target);
  579. var corners:Array = [new Point(r.x, r.y), new Point(r.right, r.y), new Point(r.right, r.bottom), new Point(r.x, r.bottom)]; //top left, top right, bottom right, bottom left
  580. for (i = corners.length - 1; i > -1; i--) {
  581. corner = _target.parent.globalToLocal(_target.localToGlobal(corners[i]));
  582. if (!(Math.abs(corner.x - _origin.x) < 1 && Math.abs(corner.y - _origin.y) < 1)) { //If the origin is on top of the corner (same coordinates), no need to factor it in.
  583. cornerAngle = TransformManager.positiveAngle(Math.atan2(corner.y - _origin.y, corner.x - _origin.x));
  584. dx = _origin.x - corner.x;
  585. dy = _origin.y - corner.y;
  586. oldLength = Math.sqrt(dx * dx + dy * dy);
  587. if (cornerAngle < _cornerAngleBR || (cornerAngle > _cornerAngleTR && _cornerAngleTR != 0)) { //Extends RIGHT
  588. dx = _bounds.right - _origin.x;
  589. newLength = (dx < 1 && ((_cornerAngleBR - cornerAngle < 0.01) || (cornerAngle - _cornerAngleTR < 0.01))) ? 0 : dx / Math.cos(cornerAngle); //Flash was occassionally reporting the angle slightly off when you put the object in the very bottom right corner and then scale inwards/upwards, and since the Math.sin() was so small, there were rounding errors in the decimals. This prevents the odd behavior.
  590. } else if (cornerAngle <= _cornerAngleBL) { //Extends DOWN
  591. dy = _bounds.bottom - _origin.y;
  592. newLength = (_cornerAngleBL - cornerAngle < 0.01) ? 0 : dy / Math.sin(cornerAngle); //Flash was occassionally reporting the angle slightly off when you put the object in the very bottom right corner and then scale inwards/upwards, and since the Math.sin() was so small, there were rounding errors in the decimals. This prevents the odd behavior.
  593. } else if (cornerAngle < _cornerAngleTL) { //Extends LEFT
  594. dx = _origin.x - _bounds.x;
  595. newLength = dx / Math.cos(cornerAngle);
  596. } else { //Extends UP
  597. dy = _origin.y - _bounds.y;
  598. newLength = dy / Math.sin(cornerAngle);
  599. }
  600. if (newLength != 0) {
  601. minScale = Math.min(minScale, Math.abs(newLength) / oldLength);
  602. }
  603. }
  604. }
  605. m = _target.transform.matrix;
  606. if (($safe.sx < 0 && (_origin.x == _bounds.x || _origin.x == _bounds.right)) || ($safe.sy < 0 && (_origin.y == _bounds.y || _origin.y == _bounds.bottom))) { //Otherwise if the origin was sitting directly on top of the bounds edge, you could scale right past it in a negative direction (flip)
  607. $safe.sx = 1;
  608. $safe.sy = 1;
  609. } else {
  610. $safe.sx = (MatrixTools.getDirectionX(m) * minScale) / MatrixTools.getDirectionX(original);
  611. $safe.sy = (MatrixTools.getDirectionY(m) * minScale) / MatrixTools.getDirectionY(original);
  612. }
  613. }
  614. }
  615. }
  616. _target.transform.matrix = original;
  617. }
  618. }
  619. /** @private **/
  620. protected function iterateStretchX($safe:Object, $angle:Number, $skew:Number):void {
  621. if (_lockScale) {
  622. $safe.sx = $safe.sy = 1;
  623. } else if (_bounds != null && $safe.sx != 1) {
  624. var original:Matrix = _target.transform.matrix;
  625. var i:uint, loops:uint, base:uint, m:Matrix = new Matrix();
  626. var inc:Number = 0.01;
  627. if ($safe.sx < 1) {
  628. inc = -0.01;
  629. }
  630. if ($safe.sx > 0) {
  631. loops = Math.abs(($safe.sx - 1) / inc) + 1;
  632. base = 1;
  633. } else {
  634. base = 0;
  635. loops = ($safe.sx / inc) + 1;
  636. }
  637. for (i = 1; i <= loops; i++) {
  638. m.a = original.a; //faster than m.clone();
  639. m.b = original.b;
  640. m.c = original.c;
  641. m.d = original.d;
  642. MatrixTools.scaleMatrix(m, base + (i * inc), 1, $angle, $skew);
  643. _target.transform.matrix = m;
  644. reposition();
  645. if (!_bounds.containsRect(_target.getBounds(_target.parent))) {
  646. if (!($safe.sx < 1 && $safe.sx > 0)) {
  647. $safe.sx = base + ((i - 1) * inc);
  648. }
  649. break;
  650. }
  651. }
  652. }
  653. }
  654. /** @private **/
  655. protected function iterateStretchY($safe:Object, $angle:Number, $skew:Number):void {
  656. if (_lockScale) {
  657. $safe.sx = $safe.sy = 1;
  658. } else if (_bounds != null && $safe.sy != 1) {
  659. var original:Matrix = _target.transform.matrix;
  660. var i:uint, loops:uint, base:uint, m:Matrix = new Matrix();
  661. var inc:Number = 0.01;
  662. if ($safe.sy < 1) {
  663. inc = -0.01;
  664. }
  665. if ($safe.sx > 0) {
  666. loops = Math.abs(($safe.sy - 1) / inc) + 1;
  667. base = 1;
  668. } else {
  669. base = 0;
  670. loops = ($safe.sy / inc) + 1;
  671. }
  672. for (i = 1; i <= loops; i++) {
  673. m.a = original.a; //faster than m.clone();
  674. m.b = original.b;
  675. m.c = original.c;
  676. m.d = original.d;
  677. MatrixTools.scaleMatrix(m, 1, base + (i * inc), $angle, $skew);
  678. _target.transform.matrix = m;
  679. reposition();
  680. if (!_bounds.containsRect(_target.getBounds(_target.parent))) {
  681. if (!($safe.sy < 1 && $safe.sy > 0)) {
  682. $safe.sy = base + ((i - 1) * inc);
  683. }
  684. break;
  685. }
  686. }
  687. }
  688. }
  689. /**
  690. * Sets minimum scaleX, maximum scaleX, minimum scaleY, and maximum scaleY
  691. *
  692. * @param $minScaleX Minimum scaleX
  693. * @param $maxScaleX Maximum scaleX
  694. * @param $minScaleY Minimum scaleY
  695. * @param $maxScaleY Maximum scaleY
  696. */
  697. public function setScaleConstraints($minScaleX:Number, $maxScaleX:Number, $minScaleY:Number, $maxScaleY:Number):void {
  698. this.minScaleX = $minScaleX;
  699. this.maxScaleX = $maxScaleX;
  700. this.minScaleY = $minScaleY;
  701. this.maxScaleY = $maxScaleY;
  702. }
  703. //---- ROTATE ------------------------------------------------------------------------------------------
  704. /**
  705. * Rotates the item by a particular angle (in Radians). This is NOT an absolute value, so if one
  706. * of the item's ro…

Large files files are truncated, but you can click here to view the full file