package com.senocular.display
{
import com.senocular.events.TransformEvent;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.EventPhase;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import mx.core.UIComponent;
import mx.core.mx_internal;
use namespace mx_internal;
/**
* Creates a transform tool that allows uaers to modify display objects on the screen
*
* @usage
* <pre>
* var tool:TransformTool = new TransformTool();
* addChild(tool);
* tool.target = targetDisplayObject;
* </pre>
*
* While scaling corners or rotating keep shift key hold down to constrain
* the transformation
*
*
* @version 0.9.10
* @author Trevor McCauley
* @author http://www.senocular.com
* @author modified by alessandro crugnola (http://www.sephiroth.it)
*/
Event"newTarget", type="com.senocular.events.TransformEvent")]
Event"transformTarget", type="com.senocular.events.TransformEvent")]
Event"transformTool", type="com.senocular.events.TransformEvent")]
Event"controlInit", type="com.senocular.events.TransformEvent")]
Event"controlTransformTool", type="com.senocular.events.TransformEvent")]
Event"controlDown", type="com.senocular.events.TransformEvent")]
Event"controlMove", type="com.senocular.events.TransformEvent")]
Event"controlUp", type="com.senocular.events.TransformEvent")]
Event"controlPreference", type="com.senocular.events.TransformEvent")]
Event"registration", type="com.senocular.events.TransformEvent")]
Event"scaleTopLeft", type="com.senocular.events.TransformEvent")]
Event"scaleTop", type="com.senocular.events.TransformEvent")]
Event"scaleTopRight", type="com.senocular.events.TransformEvent")]
Event"scaleRight", type="com.senocular.events.TransformEvent")]
Event"scaleBottomRight", type="com.senocular.events.TransformEvent")]
Event"scaleBottom", type="com.senocular.events.TransformEvent")]
Event"scaleBottomLeft", type="com.senocular.events.TransformEvent")]
Event"scaleLeft", type="com.senocular.events.TransformEvent")]
Event"rotationTopLeft", type="com.senocular.events.TransformEvent")]
Event"rotationTopRight", type="com.senocular.events.TransformEvent")]
Event"rotationBottomRight", type="com.senocular.events.TransformEvent")]
Event"rotationBottomLeft", type="com.senocular.events.TransformEvent")]
Event"skewTop", type="com.senocular.events.TransformEvent")]
Event"skewRight", type="com.senocular.events.TransformEvent")]
Event"skewBottom", type="com.senocular.events.TransformEvent")]
Event"skewLeft", type="com.senocular.events.TransformEvent")]
Event"cursorRegistration", type="com.senocular.events.TransformEvent")]
Event"cursorMove", type="com.senocular.events.TransformEvent")]
Event"cursorScale", type="com.senocular.events.TransformEvent")]
Event"cursorRotate", type="com.senocular.events.TransformEvent")]
Event"cursorSkew", type="com.senocular.events.TransformEvent")]
public class TransformTool extends UIComponent
{
private var _toolInvertedMatrix:Matrix = new Matrix();
private var _innerRegistration:Point = new Point();
private var registrationLog:Dictionary = new Dictionary(true);
private var targetBounds:Rectangle = new Rectangle();
private var mouseLoc:Point = new Point();
private var mouseOffset:Point = new Point();
private var innerMouseLoc:Point = new Point();
private var interactionStart:Point = new Point();
private var innerInteractionStart:Point = new Point();
private var interactionStartAngle:Number = 0;
private var interactionStartMatrix:Matrix = new Matrix();
private var toolSprites:Sprite = new Sprite();
private var lines:Sprite = new Sprite();
private var moveControls:Sprite = new Sprite();
private var registrationControls:Sprite = new Sprite();
private var rotateControls:Sprite = new Sprite();
private var scaleControls:Sprite = new Sprite();
private var skewControls:Sprite = new Sprite();
private var cursors:Sprite = new Sprite();
private var customControls:Sprite = new Sprite();
private var customCursors:Sprite = new Sprite();
private var _target:DisplayObject;
private var _toolMatrix:Matrix = new Matrix();
private var _globalMatrix:Matrix = new Matrix();
private var _registration:Point = new Point();
private var _livePreview:Boolean = true;
private var _raiseNewTargets:Boolean = false;
private var _moveNewTargets:Boolean = false;
private var _moveEnabled:Boolean = true;
private var _registrationEnabled:Boolean = true;
private var _rotationEnabled:Boolean = true;
private var _scaleEnabled:Boolean = true;
private var _skewEnabled:Boolean = true;
private var _outlineEnabled:Boolean = true;
private var _customControlsEnabled:Boolean = true;
private var _customCursorsEnabled:Boolean = true;
private var _cursorsEnabled:Boolean = true;
private var _rememberRegistration:Boolean = true;
private var _constrainScale:Boolean = false;
private var _constrainRotationAngle:Number = Math.PI/4;
private var _scaleWithRegistration:Boolean;
private var _moveUnderObjects:Boolean = true;
private var _maintainControlForm:Boolean = true;
private var _controlSize:uint = 12;
private var _maxScaleX:Number = Infinity;
private var _maxScaleY:Number = Infinity;
private var _boundsTopLeft:Point = new Point();
private var _boundsTop:Point = new Point();
private var _boundsTopRight:Point = new Point();
private var _boundsRight:Point = new Point();
private var _boundsBottomRight:Point = new Point();
private var _boundsBottom:Point = new Point();
private var _boundsBottomLeft:Point = new Point();
private var _boundsLeft:Point = new Point();
private var _boundsCenter:Point = new Point();
private var _currentControl:TransformToolControl;
private var _snap_function: Function;
private var _moveControl:TransformToolControl;
private var _registrationControl:TransformToolControl;
private var _outlineControl:TransformToolControl;
private var _scaleTopLeftControl:TransformToolControl;
private var _scaleTopControl:TransformToolControl;
private var _scaleTopRightControl:TransformToolControl;
private var _scaleRightControl:TransformToolControl;
private var _scaleBottomRightControl:TransformToolControl;
private var _scaleBottomControl:TransformToolControl;
private var _scaleBottomLeftControl:TransformToolControl;
private var _scaleLeftControl:TransformToolControl;
private var _rotationTopLeftControl:TransformToolControl;
private var _rotationTopRightControl:TransformToolControl;
private var _rotationBottomRightControl:TransformToolControl;
private var _rotationBottomLeftControl:TransformToolControl;
private var _skewTopControl:TransformToolControl;
private var _skewRightControl:TransformToolControl;
private var _skewBottomControl:TransformToolControl;
private var _skewLeftControl:TransformToolControl;
private var _moveCursor:TransformToolCursor;
private var _registrationCursor:TransformToolCursor;
private var _rotationCursor:TransformToolCursor;
private var _scaleCursor:TransformToolCursor;
private var _skewCursor:TransformToolCursor;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(targetChanged)
{
targetChanged = false;
targetUpdated();
registration = boundsCenter;
}
}
public function set snapFunction( f: Function ): void
{
_snap_function = f;
}
private var targetChanged:Boolean;
/**
* The display object the transform tool affects
*/
public function get target():DisplayObject
{
return _target;
}
public function setTarget( value: DisplayObject ): void
{
this.target = value;
}
public function set target(d:DisplayObject):void
{
if(d)
if (d == this || contains(d) || (d is DisplayObjectContainer && (d as DisplayObjectContainer).contains(this)))
return;
_target = d;
targetChanged = true;
invalidateSize();
invalidateDisplayList();
validateNow();
}
private function targetUpdated():void
{
if (!_target)
{
_target = null;
updateControlsVisible();
dispatchEvent(new TransformEvent(TransformEvent.NEW_TARGET));
return;
}
else
{
updateMatrix();
setNewRegistation();
updateControlsVisible();
if (_raiseNewTargets) {
raiseTarget();
}
}
if (!_moveNewTargets) {
apply();
}
dispatchEvent(new TransformEvent(TransformEvent.NEW_TARGET));
if (_moveNewTargets && _moveEnabled && _moveControl)
{
_currentControl = _moveControl;
_currentControl.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN));
}
}
/**
* When true, new targets are placed at the top of their display list
* @see target
*/
public function get raiseNewTargets():Boolean {
return _raiseNewTargets;
}
public function set raiseNewTargets(b:Boolean):void {
_raiseNewTargets = b;
}
/**
* When true, new targets are immediately given a move interaction and can be dragged
* @see target
* @see moveEnabled
*/
public function get moveNewTargets():Boolean {
return _moveNewTargets;
}
public function set moveNewTargets(b:Boolean):void {
_moveNewTargets = b;
}
/**
* When true, the target instance scales with the tool as it is transformed.
* When false, transforms in the tool are only reflected when transforms are completed.
*/
public function get livePreview():Boolean {
return _livePreview;
}
public function set livePreview(b:Boolean):void {
_livePreview = b;
}
/**
* Controls the default Control sizes of controls used by the tool
*/
public function get controlSize():uint
{
return _controlSize;
}
public function set controlSize(n:uint):void
{
if (_controlSize != n) {
_controlSize = n;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* When true, counters transformations applied to controls by their parent containers
*/
public function get maintainControlForm():Boolean {
return _maintainControlForm;
}
public function set maintainControlForm(b:Boolean):void {
if (_maintainControlForm != b) {
_maintainControlForm = b;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* When true (default), the transform tool uses an invisible control using the shape of the current
* target to allow movement. This means any objects above the target but below the
* tool cannot be clicked on since this hidden control will be clicked on first
* (allowing you to move objects below others without selecting the objects on top).
* When false, the target itself is used for movement and any objects above the target
* become clickable preventing tool movement if the target itself is not clicked directly.
*/
public function get moveUnderObjects():Boolean {
return _moveUnderObjects;
}
public function set moveUnderObjects(b:Boolean):void {
if (_moveUnderObjects != b) {
_moveUnderObjects = b;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* The transform matrix of the tool
* as it exists in its on coordinate space
* @see globalMatrix
*/
public function get toolMatrix():Matrix {
return _toolMatrix.clone();
}
public function set toolMatrix(m:Matrix):void {
updateMatrix(m, false);
updateRegistration();
dispatchEvent(new TransformEvent(TransformEvent.TRANSFORM_TOOL));
}
/**
* The transform matrix of the tool
* as it appears in global space
* @see toolMatrix
*/
public function get globalMatrix():Matrix {
var _globalMatrix:Matrix = _toolMatrix.clone();
_globalMatrix.concat(transform.concatenatedMatrix);
return _globalMatrix;
}
public function set globalMatrix(m:Matrix):void {
updateMatrix(m);
updateRegistration();
dispatchEvent(new TransformEvent(TransformEvent.TRANSFORM_TOOL));
}
/**
* The location of the registration point in the tool. Note: registration
* points are tool-specific. If you change the registration point of a
* target, the new registration will only be reflected in the tool used
* to change that point.
* @see registrationEnabled
* @see rememberRegistration
*/
public function get registration():Point {
return _registration.clone();
}
public function set registration(p:Point):void
{
_registration = p.clone();
innerRegistration = toolInvertedMatrix.transformPoint(_registration);
if (_rememberRegistration) {
registrationLog[_target] = innerRegistration;
}
invalidateDisplayList();
dispatchEvent(new TransformEvent(TransformEvent.TRANSFORM_TOOL));
}
public function get innerRegistration():Point
{
return _innerRegistration;
}
public function set innerRegistration(value:Point):void
{
_innerRegistration = value.clone();
}
public function get toolInvertedMatrix():Matrix
{
return _toolInvertedMatrix;
}
public function set toolInvertedMatrix(value:Matrix):void
{
_toolInvertedMatrix = value.clone();
}
/**
* The current control being used in the tool if being manipulated.
* This value is null if the user is not transforming the tool.
*/
public function get currentControl():TransformToolControl {
return _currentControl;
}
/**
* Allows or disallows users to move the tool
*/
public function get moveEnabled():Boolean {
return _moveEnabled;
}
public function set moveEnabled(b:Boolean):void {
if (_moveEnabled != b) {
_moveEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see and move the registration point
* @see registration
* @see rememberRegistration
*/
public function get registrationEnabled():Boolean {
return _registrationEnabled;
}
public function set registrationEnabled(b:Boolean):void {
if (_registrationEnabled != b) {
_registrationEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see and adjust rotation controls
*/
public function get rotationEnabled():Boolean {
return _rotationEnabled;
}
public function set rotationEnabled(b:Boolean):void {
if (_rotationEnabled != b) {
_rotationEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see and adjust scale controls
*/
public function get scaleEnabled():Boolean {
return _scaleEnabled;
}
public function set scaleEnabled(b:Boolean):void {
if (_scaleEnabled != b) {
_scaleEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see and adjust skew controls
*/
public function get skewEnabled():Boolean {
return _skewEnabled;
}
public function set skewEnabled(b:Boolean):void {
if (_skewEnabled != b) {
_skewEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see tool boundry outlines
*/
public function get outlineEnabled():Boolean {
return _outlineEnabled;
}
public function set outlineEnabled(b:Boolean):void {
if (_outlineEnabled != b) {
_outlineEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see native cursors
* @see addCursor
* @see removeCursor
* @see customCursorsEnabled
*/
public function get cursorsEnabled():Boolean {
return _cursorsEnabled;
}
public function set cursorsEnabled(b:Boolean):void {
if (_cursorsEnabled != b) {
_cursorsEnabled = b;
updateControlsEnabled();
}
}
/**
* Allows or disallows users to see and use custom controls
* @see addControl
* @see removeControl
* @see customCursorsEnabled
*/
public function get customControlsEnabled():Boolean {
return _customControlsEnabled;
}
public function set customControlsEnabled(b:Boolean):void {
if (_customControlsEnabled != b) {
_customControlsEnabled = b;
updateControlsEnabled();
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* Allows or disallows users to see custom cursors
* @see addCursor
* @see removeCursor
* @see cursorsEnabled
* @see customControlsEnabled
*/
public function get customCursorsEnabled():Boolean {
return _customCursorsEnabled;
}
public function set customCursorsEnabled(b:Boolean):void {
if (_customCursorsEnabled != b) {
_customCursorsEnabled = b;
updateControlsEnabled();
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* Allows or disallows users to see custom cursors
* @see registration
*/
public function get rememberRegistration():Boolean {
return _rememberRegistration;
}
public function set rememberRegistration(b:Boolean):void {
_rememberRegistration = b;
if (!_rememberRegistration) {
registrationLog = new Dictionary(true);
}
}
/**
* Allows constraining of scale transformations that scale along both X and Y.
*/
public function get constrainScale():Boolean {
return _constrainScale;
}
public function set constrainScale(b:Boolean):void {
if (_constrainScale != b) {
_constrainScale = b;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
Bindable]
/**
* Change the way the TransformTool handles with scaling
* If is set to true scaling will affect all the target sides, according with the current
* registration point. Otherwise it will only affect the current scaling control.
*
*/
public function set scaleWithRegistration(value:Boolean):void
{
if(value != _scaleWithRegistration)
{
_scaleWithRegistration = value;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
public function get scaleWithRegistration():Boolean
{
return _scaleWithRegistration;
}
/**
* The angle at which rotation is constrainged when constrainRotation is true
*/
public function get constrainRotationAngle():Number {
return _constrainRotationAngle * 180/Math.PI;
}
public function set constrainRotationAngle(n:Number):void {
var angleInRadians:Number = n * Math.PI/180;
if (_constrainRotationAngle != angleInRadians) {
_constrainRotationAngle = angleInRadians;
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_PREFERENCE));
}
}
/**
* The maximum scaleX allowed to be applied to a target
*/
public function get maxScaleX():Number {
return _maxScaleX;
}
public function set maxScaleX(n:Number):void {
_maxScaleX = n;
}
/**
* The maximum scaleY allowed to be applied to a target
*/
public function get maxScaleY():Number {
return _maxScaleY;
}
public function set maxScaleY(n:Number):void {
_maxScaleY = n;
}
public function get boundsTopLeft():Point { return _boundsTopLeft.clone(); }
public function get boundsTop():Point { return _boundsTop.clone(); }
public function get boundsTopRight():Point { return _boundsTopRight.clone(); }
public function get boundsRight():Point { return _boundsRight.clone(); }
public function get boundsBottomRight():Point { return _boundsBottomRight.clone(); }
public function get boundsBottom():Point { return _boundsBottom.clone(); }
public function get boundsBottomLeft():Point { return _boundsBottomLeft.clone(); }
public function get boundsLeft():Point { return _boundsLeft.clone(); }
public function get boundsCenter():Point { return _boundsCenter.clone(); }
public function get mouse():Point { return new Point(mouseX, mouseY); }
public function get moveControl():TransformToolControl { return _moveControl; }
public function get registrationControl():TransformToolControl { return _registrationControl; }
public function get outlineControl():TransformToolControl { return _outlineControl; }
public function get scaleTopLeftControl():TransformToolControl { return _scaleTopLeftControl; }
public function get scaleTopControl():TransformToolControl { return _scaleTopControl; }
public function get scaleTopRightControl():TransformToolControl { return _scaleTopRightControl; }
public function get scaleRightControl():TransformToolControl { return _scaleRightControl; }
public function get scaleBottomRightControl():TransformToolControl { return _scaleBottomRightControl; }
public function get scaleBottomControl():TransformToolControl { return _scaleBottomControl; }
public function get scaleBottomLeftControl():TransformToolControl { return _scaleBottomLeftControl; }
public function get scaleLeftControl():TransformToolControl { return _scaleLeftControl; }
public function get rotationTopLeftControl():TransformToolControl { return _rotationTopLeftControl; }
public function get rotationTopRightControl():TransformToolControl { return _rotationTopRightControl; }
public function get rotationBottomRightControl():TransformToolControl { return _rotationBottomRightControl; }
public function get rotationBottomLeftControl():TransformToolControl { return _rotationBottomLeftControl; }
public function get skewTopControl():TransformToolControl { return _skewTopControl; }
public function get skewRightControl():TransformToolControl { return _skewRightControl; }
public function get skewBottomControl():TransformToolControl { return _skewBottomControl; }
public function get skewLeftControl():TransformToolControl { return _skewLeftControl; }
public function get moveCursor():TransformToolCursor { return _moveCursor; }
public function get registrationCursor():TransformToolCursor { return _registrationCursor; }
public function get rotationCursor():TransformToolCursor { return _rotationCursor; }
public function get scaleCursor():TransformToolCursor { return _scaleCursor; }
public function get skewCursor():TransformToolCursor { return _skewCursor; }
/**
* TransformTool Constructor.
* Creates new instances of the transform tool
*/
public function TransformTool()
{
super();
doubleClickEnabled = true;
}
override protected function createChildren():void
{
createControls();
}
private function createControls():void
{
_moveControl = new TransformToolMoveShape("move", moveInteraction);
_registrationControl = new TransformToolRegistrationControl(TransformEvent.REGISTRATION, registrationInteraction, "registration");
_rotationTopLeftControl = new TransformToolRotateControl(TransformEvent.ROTATION_TOP_LEFT, rotationInteraction, "boundsTopLeft");
_rotationTopRightControl = new TransformToolRotateControl(TransformEvent.ROTATION_TOP_RIGHT, rotationInteraction, "boundsTopRight");
_rotationBottomRightControl = new TransformToolRotateControl(TransformEvent.ROTATION_BOTTOM_RIGHT, rotationInteraction, "boundsBottomRight");
_rotationBottomLeftControl = new TransformToolRotateControl(TransformEvent.ROTATION_BOTTOM_LEFT, rotationInteraction, "boundsBottomLeft");
_scaleTopLeftControl = new TransformToolScaleControl(TransformEvent.SCALE_TOP_LEFT, scaleTopLeftInteraction, "boundsTopLeft");
_scaleTopControl = new TransformToolScaleControl(TransformEvent.SCALE_TOP, scaleTopInteraction, "boundsTop");
_scaleTopRightControl = new TransformToolScaleControl(TransformEvent.SCALE_TOP_RIGHT, scaleTopRightInteraction, "boundsTopRight");
_scaleRightControl = new TransformToolScaleControl(TransformEvent.SCALE_RIGHT, scaleRightInteraction, "boundsRight");
_scaleBottomRightControl = new TransformToolScaleControl(TransformEvent.SCALE_BOTTOM_RIGHT, scaleBottomRightInteraction, "boundsBottomRight");
_scaleBottomControl = new TransformToolScaleControl(TransformEvent.SCALE_BOTTOM, scaleBottomInteraction, "boundsBottom");
_scaleBottomLeftControl = new TransformToolScaleControl(TransformEvent.SCALE_BOTTOM_LEFT, scaleBottomLeftInteraction, "boundsBottomLeft");
_scaleLeftControl = new TransformToolScaleControl(TransformEvent.SCALE_LEFT, scaleLeftInteraction, "boundsLeft");
_skewTopControl = new TransformToolSkewBar(TransformEvent.SKEW_TOP, skewXInteraction, "boundsTopRight", "boundsTopLeft", "boundsTopRight");
_skewRightControl = new TransformToolSkewBar(TransformEvent.SKEW_RIGHT, skewYInteraction, "boundsBottomRight","boundsTopRight", "boundsBottomRight");
_skewBottomControl = new TransformToolSkewBar(TransformEvent.SKEW_BOTTOM, skewXInteraction, "boundsBottomLeft", "boundsBottomRight","boundsBottomLeft");
_skewLeftControl = new TransformToolSkewBar(TransformEvent.SKEW_LEFT, skewYInteraction, "boundsTopLeft", "boundsBottomLeft", "boundsTopLeft");
_moveCursor = new TransformToolMoveCursor();
_moveCursor.addReference(_moveControl);
_registrationCursor = new TransformToolRegistrationCursor();
_registrationCursor.addReference(_registrationControl);
_rotationCursor = new TransformToolRotateCursor();
_rotationCursor.addReference(_rotationTopLeftControl);
_rotationCursor.addReference(_rotationTopRightControl);
_rotationCursor.addReference(_rotationBottomRightControl);
_rotationCursor.addReference(_rotationBottomLeftControl);
_scaleCursor = new TransformToolScaleCursor();
_scaleCursor.addReference(_scaleTopLeftControl);
_scaleCursor.addReference(_scaleTopControl);
_scaleCursor.addReference(_scaleTopRightControl);
_scaleCursor.addReference(_scaleRightControl);
_scaleCursor.addReference(_scaleBottomRightControl);
_scaleCursor.addReference(_scaleBottomControl);
_scaleCursor.addReference(_scaleBottomLeftControl);
_scaleCursor.addReference(_scaleLeftControl);
_skewCursor = new TransformToolSkewCursor();
_skewCursor.addReference(_skewTopControl);
_skewCursor.addReference(_skewRightControl);
_skewCursor.addReference(_skewBottomControl);
_skewCursor.addReference(_skewLeftControl);
addToolControl(moveControls, _moveControl);
addToolControl(registrationControls, _registrationControl);
addToolControl(rotateControls, _rotationTopLeftControl);
addToolControl(rotateControls, _rotationTopRightControl);
addToolControl(rotateControls, _rotationBottomRightControl);
addToolControl(rotateControls, _rotationBottomLeftControl);
addToolControl(scaleControls, _scaleTopControl);
addToolControl(scaleControls, _scaleRightControl);
addToolControl(scaleControls, _scaleBottomControl);
addToolControl(scaleControls, _scaleLeftControl);
addToolControl(scaleControls, _scaleTopLeftControl);
addToolControl(scaleControls, _scaleTopRightControl);
addToolControl(scaleControls, _scaleBottomRightControl);
addToolControl(scaleControls, _scaleBottomLeftControl);
addToolControl(skewControls, _skewTopControl);
addToolControl(skewControls, _skewRightControl);
addToolControl(skewControls, _skewBottomControl);
addToolControl(skewControls, _skewLeftControl);
addToolControl(lines, new TransformToolOutline("outline"), false);
addToolControl(cursors, _moveCursor, false);
addToolControl(cursors, _registrationCursor, false);
addToolControl(cursors, _rotationCursor, false);
addToolControl(cursors, _scaleCursor, false);
addToolControl(cursors, _skewCursor, false);
updateControlsEnabled();
}
private function addToolControl(container:Sprite, control:TransformToolControl, interactive:Boolean = true):void
{
control.transformTool = this;
if (interactive)
{
control.addEventListener(MouseEvent.MOUSE_DOWN, startInteractionHandler);
}
container.addChild(control);
control.dispatchEvent(new TransformEvent(TransformEvent.CONTROL_INIT));
control.doubleClickEnabled = true;
}
/**
* Allows you to add a custom control to the tool
* @see removeControl
* @see addCursor
* @see removeCursor
*/
public function addControl(control:TransformToolControl):void {
addToolControl(customControls, control);
}
/**
* Allows you to remove a custom control to the tool
* @see addControl
* @see addCursor
* @see removeCursor
*/
public function removeControl(control:TransformToolControl):TransformToolControl {
if (customControls.contains(control)) {
customControls.removeChild(control);
return control;
}
return null;
}
/**
* Allows you to add a custom cursor to the tool
* @see removeCursor
* @see addControl
* @see removeControl
*/
public function addCursor(cursor:TransformToolCursor):void {
addToolControl(customCursors, cursor);
}
/**
* Allows you to remove a custom cursor to the tool
* @see addCursor
* @see addControl
* @see removeControl
*/
public function removeCursor(cursor:TransformToolCursor):TransformToolCursor {
if (customCursors.contains(cursor)) {
customCursors.removeChild(cursor);
return cursor;
}
return null;
}
/**
* Allows you to change the appearance of default controls
* @see addControl
* @see removeControl
*/
public function setSkin( controlName: String, skin: DisplayObject ): void
{
var control:TransformToolInternalControl = getControlByName( controlName );
if ( control )
{
control.skin = skin;
}
}
/**
* Allows you to get the skin of an existing control.
* If one was not set, null is returned
* @see addControl
* @see removeControl
*/
public function getSkin(controlName:String):DisplayObject {
var control:TransformToolInternalControl = getControlByName(controlName);
return control.skin;
}
private function getControlByName(controlName:String):TransformToolInternalControl {
var control:TransformToolInternalControl;
var containers:Array = new Array(skewControls, registrationControls, cursors, rotateControls, scaleControls);
var i:int = containers.length;
while (i-- && control == null) {
control = containers[i].getChildByName(controlName) as TransformToolInternalControl;
}
return control;
}
private function startInteractionHandler(event:MouseEvent):void
{
_currentControl = event.currentTarget as TransformToolControl;
if (_currentControl)
setupInteraction();
}
private function setupInteraction():void
{
updateMatrix();
apply();
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_DOWN));
mouseOffset = (_currentControl && _currentControl.referencePoint) ? _currentControl.referencePoint.subtract(new Point(mouseX, mouseY)) : new Point(0, 0);
updateMouse();
interactionStart = mouseLoc.clone();
innerInteractionStart = innerMouseLoc.clone();
interactionStartMatrix = _toolMatrix.clone();
interactionStartAngle = distortAngle();
if ( stage )
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, interactionHandler, false, 0, true);
stage.addEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, false, 0, true);
stage.addEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, true, 0, true);
}
}
private function interactionHandler(event:MouseEvent):void
{
updateMouse();
_toolMatrix = interactionStartMatrix.clone();
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_MOVE, false, false, event.localX, event.localY, event.relatedObject, event.ctrlKey, event.altKey, event.shiftKey, event.buttonDown, event.delta));
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_TRANSFORM_TOOL, false, false, event.localX, event.localY, event.relatedObject, event.ctrlKey, event.altKey, event.shiftKey, event.buttonDown, event.delta));
if (_livePreview) {
apply();
}
event.updateAfterEvent();
}
private function endInteractionHandler(event:MouseEvent):void
{
if (event.eventPhase == EventPhase.BUBBLING_PHASE || !(event.currentTarget is Stage)) {
return;
}
if (!_livePreview) {
apply();
}
var stageRef:Stage = event.currentTarget as Stage;
stageRef.removeEventListener(MouseEvent.MOUSE_MOVE, interactionHandler);
stageRef.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, false);
stageRef.removeEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, true);
dispatchEvent(new TransformEvent(TransformEvent.CONTROL_UP, false, false, event.localX, event.localY, event.relatedObject, event.ctrlKey, event.altKey, event.shiftKey, event.buttonDown, event.delta));
_currentControl = null;
}
/**
* Control Interaction. Moves the tool
*/
public function moveInteraction(event:TransformEvent):void
{
var moveLoc:Point = mouseLoc.subtract(interactionStart);
_toolMatrix.tx += moveLoc.x;
_toolMatrix.ty += moveLoc.y;
updateRegistration();
completeInteraction();
}
/**
* Control Interaction. Moves the registration point
*/
public function registrationInteraction(event:TransformEvent):void
{
_registration.x = mouseLoc.x;
_registration.y = mouseLoc.y;
innerRegistration = toolInvertedMatrix.transformPoint(_registration);
if (_rememberRegistration) {
registrationLog[_target] = innerRegistration;
}
completeInteraction();
}
/**
* Control Interaction. Rotates the tool
*/
public function rotationInteraction(event:TransformEvent):void
{
var globalMatrix:Matrix = transform.concatenatedMatrix;
var globalInvertedMatrix:Matrix = globalMatrix.clone();
globalInvertedMatrix.invert();
_toolMatrix.concat(globalMatrix);
var angle:Number = distortAngle() - interactionStartAngle;
if (event.shiftKey)
{
if (angle > Math.PI)
angle -= Math.PI*2;
else if (angle < -Math.PI)
angle += Math.PI*2;
angle = Math.round(angle/_constrainRotationAngle)*_constrainRotationAngle;
}
_toolMatrix.rotate(angle);
_toolMatrix.concat(globalInvertedMatrix);
completeInteraction(true);
}
/**
* Scale left
*/
mx_internal function scaleLeftInteraction( event: TransformEvent ): void
{
var distortPoint:Point = distortOffset(
new Point( innerMouseLoc.x, innerInteractionStart.y),
innerInteractionStart.x - (_scaleWithRegistration ? innerRegistration.x : targetBounds.right )
);
var oldBounds:Point = boundsRight.clone( );
var old:Point = registration.clone();
_toolMatrix.a += distortPoint.x;
_toolMatrix.b += distortPoint.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsRight ).x;
_toolMatrix.ty += oldBounds.subtract( boundsRight ).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Scale Right
*/
mx_internal function scaleRightInteraction( event: TransformEvent ): void
{
var distortPoint: Point = distortOffset(
new Point( innerMouseLoc.x, innerInteractionStart.y ),
innerInteractionStart.x - (_scaleWithRegistration ? innerRegistration.x : targetBounds.left )
);
var oldBounds: Point = boundsLeft.clone( );
_toolMatrix.a += distortPoint.x;
_toolMatrix.b += distortPoint.y;
if( !_scaleWithRegistration )
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsLeft ).x;
_toolMatrix.ty += oldBounds.subtract( boundsLeft ).y;
completeInteraction( false );
updateRegistration( );
} else {
completeInteraction( true );
}
}
/**
* Scale Top registration
*
*/
mx_internal function scaleTopInteraction( event: TransformEvent ): void
{
var distortPoint:Point = distortOffset(
new Point(
innerInteractionStart.x, innerMouseLoc.y),
innerInteractionStart.y - (_scaleWithRegistration ? innerRegistration.y : targetBounds.bottom)
);
var oldBounds:Point = boundsBottom.clone();
_toolMatrix.c += distortPoint.x;
_toolMatrix.d += distortPoint.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsBottom ).x;
_toolMatrix.ty += oldBounds.subtract( boundsBottom ).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Scale Bottom
*/
mx_internal function scaleBottomInteraction(event:TransformEvent):void
{
var distortPoint:Point = distortOffset(
new Point(
innerInteractionStart.x, innerMouseLoc.y ),
innerInteractionStart.y - ( _scaleWithRegistration ? innerRegistration.y : targetBounds.top )
);
var oldBounds:Point = boundsTop.clone();
_toolMatrix.c += distortPoint.x;
_toolMatrix.d += distortPoint.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsTop ).x;
_toolMatrix.ty += oldBounds.subtract( boundsTop ).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Scale bottom left
*/
mx_internal function scaleBottomLeftInteraction( event:TransformEvent ): void
{
var innerMouseRef: Point = innerMouseLoc.clone( );
if ( event.shiftKey )
{
var moved: Point = innerMouseLoc.subtract( innerInteractionStart );
var regOffset: Point = innerInteractionStart.subtract( _scaleWithRegistration ? innerRegistration : new Point( targetBounds.right, targetBounds.top ) );
var ratioH: Number = regOffset.x ? moved.x/regOffset.x : 0;
var ratioV: Number = regOffset.y ? moved.y/regOffset.y : 0;
if ( ratioH > ratioV )
{
innerMouseRef.x = innerInteractionStart.x + regOffset.x * ratioV;
} else {
innerMouseRef.y = innerInteractionStart.y + regOffset.y * ratioH;
}
}
var oldBounds: Point = boundsTopRight.clone( );
var distortH: Point = distortOffset( new Point( innerMouseRef.x, innerInteractionStart.y ), innerInteractionStart.x - ( _scaleWithRegistration ? innerRegistration.x : targetBounds.right ) );
var distortV: Point = distortOffset( new Point( innerInteractionStart.x, innerMouseRef.y ), innerInteractionStart.y - ( _scaleWithRegistration ? innerRegistration.y : targetBounds.top ) );
_toolMatrix.a += distortH.x;
_toolMatrix.b += distortH.y;
_toolMatrix.c += distortV.x;
_toolMatrix.d += distortV.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract(boundsTopRight).x;
_toolMatrix.ty += oldBounds.subtract(boundsTopRight).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Scale bottom right
*/
mx_internal function scaleBottomRightInteraction( event: TransformEvent ): void
{
var innerMouseRef: Point = innerMouseLoc.clone( );
if( event.shiftKey )
{
var moved: Point = innerMouseLoc.subtract( innerInteractionStart );
var regOffset:Point = innerInteractionStart.subtract( _scaleWithRegistration ? innerRegistration : new Point( targetBounds.left, targetBounds.top ) );
var ratioH:Number = regOffset.x ? moved.x/regOffset.x : 0;
var ratioV:Number = regOffset.y ? moved.y/regOffset.y : 0;
if (ratioH > ratioV)
{
innerMouseRef.x = innerInteractionStart.x + regOffset.x * ratioV;
} else {
innerMouseRef.y = innerInteractionStart.y + regOffset.y * ratioH;
}
}
var oldBounds: Point = boundsTopLeft.clone( );
var distortH: Point = distortOffset( new Point( innerMouseRef.x, innerInteractionStart.y ), innerInteractionStart.x - ( _scaleWithRegistration ? innerRegistration.x : targetBounds.left) );
var distortV: Point = distortOffset( new Point( innerInteractionStart.x, innerMouseRef.y ), innerInteractionStart.y - ( _scaleWithRegistration ? innerRegistration.y : targetBounds.top ) );
_toolMatrix.a += distortH.x;
_toolMatrix.b += distortH.y;
_toolMatrix.c += distortV.x;
_toolMatrix.d += distortV.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsTopLeft ).x;
_toolMatrix.ty += oldBounds.subtract( boundsTopLeft ).y;
completeInteraction( false );
updateRegistration();
}
}
/**
* Scale top left
*/
mx_internal function scaleTopLeftInteraction( event: TransformEvent ): void
{
var innerMouseRef:Point = innerMouseLoc.clone( );
if ( event.shiftKey )
{
var moved:Point = innerMouseLoc.subtract(innerInteractionStart);
var regOffset:Point = innerInteractionStart.subtract( _scaleWithRegistration ? innerRegistration : new Point(targetBounds.right, targetBounds.bottom ) );
var ratioH:Number = regOffset.x ? moved.x/regOffset.x : 0;
var ratioV:Number = regOffset.y ? moved.y/regOffset.y : 0;
if ( ratioH > ratioV )
{
innerMouseRef.x = innerInteractionStart.x + regOffset.x * ratioV;
} else {
innerMouseRef.y = innerInteractionStart.y + regOffset.y * ratioH;
}
}
var oldBounds:Point = boundsBottomRight.clone( );
var distortH:Point = distortOffset(new Point(innerMouseRef.x, innerInteractionStart.y), innerInteractionStart.x - (_scaleWithRegistration ? innerRegistration.x : targetBounds.right));
var distortV:Point = distortOffset(new Point(innerInteractionStart.x, innerMouseRef.y), innerInteractionStart.y - (_scaleWithRegistration ? innerRegistration.y : targetBounds.bottom));
_toolMatrix.a += distortH.x;
_toolMatrix.b += distortH.y;
_toolMatrix.c += distortV.x;
_toolMatrix.d += distortV.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsBottomRight ).x;
_toolMatrix.ty += oldBounds.subtract( boundsBottomRight ).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Scale top right
*/
mx_internal function scaleTopRightInteraction( event: TransformEvent ): void
{
var innerMouseRef:Point = innerMouseLoc.clone( );
if ( event.shiftKey )
{
var moved: Point = innerMouseLoc.subtract( innerInteractionStart );
var regOffset: Point = innerInteractionStart.subtract( _scaleWithRegistration ? innerRegistration : new Point( targetBounds.left, targetBounds.bottom ) );
var ratioH: Number = regOffset.x ? moved.x/regOffset.x : 0;
var ratioV: Number = regOffset.y ? moved.y/regOffset.y : 0;
if ( ratioH > ratioV )
{
innerMouseRef.x = innerInteractionStart.x + regOffset.x * ratioV;
} else {
innerMouseRef.y = innerInteractionStart.y + regOffset.y * ratioH;
}
}
var oldBounds: Point = boundsBottomLeft.clone( );
var distortH: Point = distortOffset( new Point( innerMouseRef.x, innerInteractionStart.y ), innerInteractionStart.x - ( _scaleWithRegistration ? innerRegistration.x : targetBounds.left ) );
var distortV: Point = distortOffset( new Point( innerInteractionStart.x, innerMouseRef.y ), innerInteractionStart.y - ( _scaleWithRegistration ? innerRegistration.y : targetBounds.bottom ) );
_toolMatrix.a += distortH.x;
_toolMatrix.b += distortH.y;
_toolMatrix.c += distortV.x;
_toolMatrix.d += distortV.y;
if( _scaleWithRegistration )
{
completeInteraction( true );
} else
{
completeInteraction( false );
_toolMatrix.tx += oldBounds.subtract( boundsBottomLeft ).x;
_toolMatrix.ty += oldBounds.subtract( boundsBottomLeft ).y;
completeInteraction( false );
updateRegistration( );
}
}
/**
* Control Interaction. Skews the tool along the X axis
*/
public function skewXInteraction(event:TransformEvent):void
{
var distortH:Point = distortOffset(new Point(innerMouseLoc.x, innerInteractionStart.y), innerInteractionStart.y - innerRegistration.y);
_toolMatrix.c += distortH.x;
_toolMatrix.d += distortH.y;
completeInteraction(true);
}
/**
* Control Interaction. Skews the tool along the Y axis
*/
public function skewYInteraction(event:TransformEvent):void
{
var distortV:Point = distortOffset(new Point(innerInteractionStart.x, innerMouseLoc.y), innerInteractionStart.x - innerRegistration.x);
_toolMatrix.a += distortV.x;
_toolMatrix.b += distortV.y;
completeInteraction(true);
}
private function distortOffset(offset:Point, regDiff:Number):Point {
var ratioH:Number = regDiff ? targetBounds.width/regDiff : 0;
var ratioV:Number = regDiff ? targetBounds.height/regDiff : 0;
offset = interactionStartMatrix.transformPoint(offset).subtract(interactionStart);
offset.x *= targetBounds.width ? ratioH/targetBounds.width : 0;
offset.y *= targetBounds.height ? ratioV/targetBounds.height : 0;
return offset;
}
private function completeInteraction( offsetReg: Boolean = false ): void
{
enforceLimits( );
if (offsetReg) {
var offset:Point = _registration.subtract(_toolMatrix.transformPoint(innerRegistration));
_toolMatrix.tx += offset.x;
_toolMatrix.ty += offset.y;
}
updateBounds();
}
private function distortAngle():Number {
var globalMatrix:Matrix = transform.concatenatedMatrix;
var gMouseLoc:Point = globalMatrix.transformPoint(mouseLoc);
var gRegistration:Point = globalMatrix.transformPoint(_registration);
var offset:Point = gMouseLoc.subtract(gRegistration);
return Math.atan2(offset.y, offset.x);
}
private function updateMouse():void {
mouseLoc = new Point(mouseX, mouseY).add(mouseOffset);
if( _snap_function != null )
{
mouseLoc = _snap_function( mouseLoc );
}
innerMouseLoc = toolInvertedMatrix.transformPoint(mouseLoc);
}
private function updateMatrix(useMatrix:Matrix = null, counterTransform:Boolean = true):void
{
if (_target) {
_toolMatrix = useMatrix ? useMatrix.clone() : _target.transform.concatenatedMatrix.clone();
if (counterTransform)
{
var current:Matrix = transform.concatenatedMatrix;
current.invert();
_toolMatrix.concat(current);
}
enforceLimits();
toolInvertedMatrix = _toolMatrix.clone();
toolInvertedMatrix.invert();
updateBounds();
}
}
private function updateBounds():void
{
if (_target) {
targetBounds = _target.getBounds(_target);
_boundsTopLeft = _toolMatrix.transformPoint(new Point(targetBounds.left, targetBounds.top));
_boundsTopRight = _toolMatrix.transformPoint(new Point(targetBounds.right, targetBounds.top));
_boundsBottomRight = _toolMatrix.transformPoint(new Point(targetBounds.right, targetBounds.bottom));
_boundsBottomLeft = _toolMatrix.transformPoint(new Point(targetBounds.left, targetBounds.bottom));
_boundsTop = Point.interpolate(_boundsTopLeft, _boundsTopRight, .5);
_boundsRight = Point.interpolate(_boundsTopRight, _boundsBottomRight, .5);
_boundsBottom = Point.interpolate(_boundsBottomRight, _boundsBottomLeft, .5);
_boundsLeft = Point.interpolate(_boundsBottomLeft, _boundsTopLeft, .5);
_boundsCenter = Point.interpolate(_boundsTopLeft, _boundsBottomRight, .5);
}
}
private function updateControlsVisible():void {
var isChild:Boolean = contains(toolSprites);
if (_target) {
if (!isChild) {
addChild(toolSprites);
}
}else if (isChild) {
removeChild(toolSprites);
}
}
private function updateControlsEnabled():void {
updateControlContainer(customCursors, _customCursorsEnabled);
updateControlContainer(cursors, _cursorsEnabled);
updateControlContainer(customControls, _customControlsEnabled);
updateControlContainer(registrationControls, _registrationEnabled);
updateControlContainer(scaleControls, _scaleEnabled);
updateControlContainer(skewControls, _skewEnabled);
updateControlContainer(moveControls, _moveEnabled);
updateControlContainer(rotateControls, _rotationEnabled);
updateControlContainer(lines, _outlineEnabled);
}
private function updateControlContainer(container:Sprite, enabled:Boolean):void {
var isChild:Boolean = toolSprites.contains(container);
if (enabled) {
if (isChild) {
toolSprites.setChildIndex(container, 0);
}else{
toolSprites.addChildAt(container, 0);
}
}else if (isChild) {
toolSprites.removeChild(container);
}
}
private function updateRegistration():void {
_registration = _toolMatrix.transformPoint(innerRegistration);
}
private function enforceLimits():void {
var currScale:Number;
var angle:Number;
var enforced:Boolean = false;
var _globalMatrix:Matrix = _toolMatrix.clone();
_globalMatrix.concat(transform.concatenatedMatrix);
currScale = Math.sqrt(_globalMatrix.a * _globalMatrix.a + _globalMatrix.b * _globalMatrix.b);
if (currScale > _maxScaleX) {
angle = Math.atan2(_globalMatrix.b, _globalMatrix.a);
_globalMatrix.a = Math.cos(angle) * _maxScaleX;
_globalMatrix.b = Math.sin(angle) * _maxScaleX;
enforced = true;
}
currScale = Math.sqrt(_globalMatrix.c * _globalMatrix.c + _globalMatrix.d * _globalMatrix.d);
if (currScale > _maxScaleY) {
angle= Math.atan2(_globalMatrix.c, _globalMatrix.d);
_globalMatrix.d = Math.cos(angle) * _maxScaleY;
_globalMatrix.c = Math.sin(angle) * _maxScaleY;
enforced = true;
}
if (enforced) {
_toolMatrix = _globalMatrix;
var current:Matrix = transform.concatenatedMatrix;
current.invert();
_toolMatrix.concat(current);
}
}
private function setNewRegistation():void
{
if (_rememberRegistration && _target in registrationLog) {
var savedReg:Point = registrationLog[_target];
innerRegistration = registrationLog[_target];
} else {
innerRegistration = new Point(0, 0);
}
updateRegistration();
}
private function raiseTarget():void
{
var index:int = _target.parent.numChildren - 1;
_target.parent.setChildIndex(_target, index);
if (_target.parent == parent) {
parent.setChildIndex(this, index);
}
}
/**
* Draws the transform tool over its target instance
*/
public function draw():void {
updateMatrix();
dispatchEvent(new TransformEvent(TransformEvent.TRANSFORM_TOOL));
}
/**
* Applies the current tool transformation to its target instance
*/
public function apply():void {
if (_target) {
var applyMatrix:Matrix = _toolMatrix.clone();
applyMatrix.concat(transform.concatenatedMatrix);
if (_target.parent) {
var invertMatrix:Matrix = target.parent.transform.concatenatedMatrix;
invertMatrix.invert();
applyMatrix.concat(invertMatrix);
}
_target.transform.matrix = applyMatrix;
dispatchEvent(new TransformEvent(TransformEvent.TRANSFORM_TARGET));
}
}
}
}