TransformTool.as

package com.senocular.display {
    
    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.EventPhase;
    import flash.events.MouseEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.geom.Transform;
    import flash.utils.Dictionary;
    
    
// TODO: Documentation
    
// TODO: Handle 0-size transformations
    
    
/**
     
* 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>
     
* 
     
* @version 0.9.10
     
* @author  Trevor McCauley
     
* @author  http://www.senocular.com
     
*/
    
public class TransformTool extends Sprite {
        
        
// Variables
        
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();
        
        
// With getter/setters
        
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 = true;
        
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// default at 45 degrees
        
private var _constrainRotation:Boolean = false;
        
        
private var _moveUnderObjects:Boolean = true;
        
private var _maintainControlForm:Boolean = true;
        
private var _controlSize:Number = 8;
            
        
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 _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;
        
        
// Event constants
        
public static const NEW_TARGET:String = "newTarget";
        
public static const TRANSFORM_TARGET:String = "transformTarget";
        
public static const TRANSFORM_TOOL:String = "transformTool";
        
public static const CONTROL_INIT:String = "controlInit";
        
public static const CONTROL_TRANSFORM_TOOL:String = "controlTransformTool";
        
public static const CONTROL_DOWN:String = "controlDown";
        
public static const CONTROL_MOVE:String = "controlMove";
        
public static const CONTROL_UP:String = "controlUp";
        
public static const CONTROL_PREFERENCE:String = "controlPreference";
        
        
// Skin constants
        
public static const REGISTRATION:String = "registration";
        
public static const SCALE_TOP_LEFT:String = "scaleTopLeft";
        
public static const SCALE_TOP:String = "scaleTop";
        
public static const SCALE_TOP_RIGHT:String = "scaleTopRight";
        
public static const SCALE_RIGHT:String = "scaleRight";
        
public static const SCALE_BOTTOM_RIGHT:String = "scaleBottomRight";
        
public static const SCALE_BOTTOM:String = "scaleBottom";
        
public static const SCALE_BOTTOM_LEFT:String = "scaleBottomLeft";
        
public static const SCALE_LEFT:String = "scaleLeft";
        
public static const ROTATION_TOP_LEFT:String = "rotationTopLeft";
        
public static const ROTATION_TOP_RIGHT:String = "rotationTopRight";
        
public static const ROTATION_BOTTOM_RIGHT:String = "rotationBottomRight";
        
public static const ROTATION_BOTTOM_LEFT:String = "rotationBottomLeft";
        
public static const SKEW_TOP:String = "skewTop";
        
public static const SKEW_RIGHT:String = "skewRight";
        
public static const SKEW_BOTTOM:String = "skewBottom";
        
public static const SKEW_LEFT:String = "skewLeft";
        
public static const CURSOR_REGISTRATION:String = "cursorRegistration";
        
public static const CURSOR_MOVE:String = "cursorMove";
        
public static const CURSOR_SCALE:String = "cursorScale";
        
public static const CURSOR_ROTATION:String = "cursorRotate";
        
public static const CURSOR_SKEW:String = "cursorSkew";
        
        
// Properties
        
        
/**
         
* The display object the transform tool affects
         
*/
        
public function get target():DisplayObject {
            return _target;
        }
        
public function set target(d:DisplayObject):void {
            
            
// null target, set target as null
            
if (!d) {
                
if (_target) {
                    _target 
= null;
                    updateControlsVisible();
                    dispatchEvent(
new Event(NEW_TARGET));
                }
                return;
            }
else{
                
                
// invalid target, do nothing
                
if (d == _target || d == this || contains(d)
                || (d 
is DisplayObjectContainer && (d as DisplayObjectContainer).contains(this))) {
                    return;
                }
                
                
// valid target, set and update
                _target 
= d;
                updateMatrix();
                setNewRegistation();
                updateControlsVisible();
                
                
// raise to top of display list if applies
                
if (_raiseNewTargets) {
                    raiseTarget();
                }
            }
            
            
// if not moving new targets, apply transforms
            
if (!_moveNewTargets) {
                apply();
            }
            
            
// send event; updates control points
            dispatchEvent(
new Event(NEW_TARGET));
                
            
// initiate move interaction if applies after controls updated
            
if (_moveNewTargets && _moveEnabled && _moveControl) {
                _currentControl 
= _moveControl;
                _currentControl.dispatchEvent(
new MouseEvent(MouseEvent.MOUSE_DOWN));
            }
        }
        
        
/**
         
* When truenew 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 truenew 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():Number {
            return _controlSize;
        }
        
public function set controlSize(n:Number):void {
            
if (_controlSize != n) {
                _controlSize 
= n;
                dispatchEvent(
new Event(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 Event(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 Event(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 Event(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 Event(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) {
                
// log new registration point for the next
                
// time this target is selected
                registrationLog[_target] 
= innerRegistration;
            }
            dispatchEvent(
new Event(TRANSFORM_TOOL));
        }
        
        
/**
         
* 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 Event(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 Event(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.
         
* @see constrainRotation
         
*/
        
public function get constrainScale():Boolean {
            return _constrainScale;
        }
        
public function set constrainScale(b:Boolean):void {
            
if (_constrainScale != b) {
                _constrainScale 
= b;
                dispatchEvent(
new Event(CONTROL_PREFERENCE));
            }
        }
        
        
/**
         
* Allows constraining of rotation transformations by an angle
         
* @see constrainRotationAngle
         
* @see constrainScale
         
*/
        
public function get constrainRotation():Boolean {
            return _constrainRotation;
        }
        
public function set constrainRotation(b:Boolean):void {
            
if (_constrainRotation != b) {
                _constrainRotation 
= b;
                dispatchEvent(
new Event(CONTROL_PREFERENCE));
            }
        }
        
        
/**
         
* The angle at which rotation is constrainged when constrainRotation is true
         
* @see constrainRotation
         
*/
        
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 Event(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() {
            createControls();
        }
        
        
/**
         
* Provides a string representation of the transform instance
         
*/
        override 
public function toString():String {
            return 
"[Transform Tool: target=" + String(_target) + "]" ;
        }
        
        
// Setup
        
private function createControls():void {
            
            
// defining controls
            _moveControl 
= new TransformToolMoveShape("move", moveInteraction);
            _registrationControl 
= new TransformToolRegistrationControl(REGISTRATION, registrationInteraction, "registration");
            _rotationTopLeftControl 
= new TransformToolRotateControl(ROTATION_TOP_LEFT, rotationInteraction, "boundsTopLeft");
            _rotationTopRightControl 
= new TransformToolRotateControl(ROTATION_TOP_RIGHT, rotationInteraction, "boundsTopRight");
            _rotationBottomRightControl 
= new TransformToolRotateControl(ROTATION_BOTTOM_RIGHT, rotationInteraction, "boundsBottomRight");
            _rotationBottomLeftControl 
= new TransformToolRotateControl(ROTATION_BOTTOM_LEFT, rotationInteraction, "boundsBottomLeft");
            _scaleTopLeftControl 
= new TransformToolScaleControl(SCALE_TOP_LEFT, scaleBothInteraction, "boundsTopLeft");
            _scaleTopControl 
= new TransformToolScaleControl(SCALE_TOP, scaleYInteraction, "boundsTop");
            _scaleTopRightControl 
= new TransformToolScaleControl(SCALE_TOP_RIGHT, scaleBothInteraction, "boundsTopRight");
            _scaleRightControl 
= new TransformToolScaleControl(SCALE_RIGHT, scaleXInteraction, "boundsRight");
            _scaleBottomRightControl 
= new TransformToolScaleControl(SCALE_BOTTOM_RIGHT, scaleBothInteraction, "boundsBottomRight");
            _scaleBottomControl 
= new TransformToolScaleControl(SCALE_BOTTOM, scaleYInteraction, "boundsBottom");
            _scaleBottomLeftControl 
= new TransformToolScaleControl(SCALE_BOTTOM_LEFT, scaleBothInteraction, "boundsBottomLeft");
            _scaleLeftControl 
= new TransformToolScaleControl(SCALE_LEFT, scaleXInteraction, "boundsLeft");
            _skewTopControl 
= new TransformToolSkewBar(SKEW_TOP, skewXInteraction, "boundsTopRight""boundsTopLeft""boundsTopRight");
            _skewRightControl 
= new TransformToolSkewBar(SKEW_RIGHT, skewYInteraction, "boundsBottomRight""boundsTopRight""boundsBottomRight");
            _skewBottomControl 
= new TransformToolSkewBar(SKEW_BOTTOM, skewXInteraction, "boundsBottomLeft""boundsBottomRight""boundsBottomLeft");
            _skewLeftControl 
= new TransformToolSkewBar(SKEW_LEFT, skewYInteraction, "boundsTopLeft""boundsBottomLeft""boundsTopLeft");
            
            
// defining cursors
            _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);
            
            
// adding controls
            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);
            
            
// adding cursors
            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 Event(CONTROL_INIT));
        }
        
        
/**
         
* 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 setnull 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;
        }
        
        
// Interaction Handlers
        
private function startInteractionHandler(event:MouseEvent):void {
            _currentControl 
= event.currentTarget as TransformToolControl;
            
if (_currentControl) {
                setupInteraction();
            }
        }
        
        
private function setupInteraction():void {
            updateMatrix();
            apply();
            dispatchEvent(
new Event(CONTROL_DOWN));
            
            
// mouse offset to allow interaction from desired point
            mouseOffset 
= (_currentControl && _currentControl.referencePoint) ? _currentControl.referencePoint.subtract(new Point(mouseX, mouseY)) : new Point(00);
            updateMouse();
            
            
// set variables for interaction reference
            interactionStart 
= mouseLoc.clone();
            innerInteractionStart 
= innerMouseLoc.clone();
            interactionStartMatrix 
= _toolMatrix.clone();
            interactionStartAngle 
= distortAngle();
            
            
if (stage) {
                
// setup stage events to manage control interaction
                stage.addEventListener(MouseEvent.MOUSE_MOVE, interactionHandler);
                stage.addEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, 
false);
                stage.addEventListener(MouseEvent.MOUSE_UP, endInteractionHandler, 
true);
            }
        }
        
        
private function interactionHandler(event:MouseEvent):void {
            
// define mouse position for interaction
            updateMouse();
            
            
// use original toolMatrix for reference of interaction
            _toolMatrix 
= interactionStartMatrix.clone();
            
            
// dispatch events that let controls do their thing
            dispatchEvent(
new Event(CONTROL_MOVE));
            dispatchEvent(
new Event(CONTROL_TRANSFORM_TOOL));
            
            
if (_livePreview) {
                
// update target if applicable
                apply();
            }
            
            
// smooth sailing
            event.updateAfterEvent();
        }
        
        
private function endInteractionHandler(event:MouseEvent):void {
            
if (event.eventPhase == EventPhase.BUBBLING_PHASE || !(event.currentTarget is Stage)) {
                
// ignore unrelated events received by stage
                return;
            }
            
            
if (!_livePreview) {
                
// update target if applicable
                apply();
            }
            
            
// get stage reference from event in case
            
// stage is no longer accessible from this instance
            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 Event(CONTROL_UP));
            _currentControl 
= null;
        }
        
        
// Interaction Transformations
        
/**
         
* Control Interaction.  Moves the tool
         
*/
        
public function moveInteraction():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():void {
            
// move registration point
            _registration.x 
= mouseLoc.x;
            _registration.y 
= mouseLoc.y;
            innerRegistration 
= toolInvertedMatrix.transformPoint(_registration);
            
            
if (_rememberRegistration) {
                
// log new registration point for the next
                
// time this target is selected
                registrationLog[_target] 
= innerRegistration;
            }
            completeInteraction();
        }
        
        
/**
         
* Control Interaction.  Rotates the tool
         
*/
        
public function rotationInteraction():void {
            
// rotate in global transform
            var globalMatrix:Matrix 
= transform.concatenatedMatrix;
            var globalInvertedMatrix:Matrix 
= globalMatrix.clone();
            globalInvertedMatrix.invert();
            _toolMatrix.concat(globalMatrix);
            
            
// get change in rotation
            var angle:Number 
= distortAngle() - interactionStartAngle;
            
            
if (_constrainRotation) {
                
// constrain rotation based on constrainRotationAngle
                
if (angle > Math.PI) {
                    angle 
-= Math.PI*2;
                }
else if (angle < -Math.PI) {
                    angle 
+= Math.PI*2;
                }
                angle 
= Math.round(angle/_constrainRotationAngle)*_constrainRotationAngle;
            }
            
            
// apply rotation to toolMatrix
            _toolMatrix.rotate(angle);
            
            _toolMatrix.concat(globalInvertedMatrix);
            completeInteraction(
true);
        }
        
        
/**
         
* Control Interaction.  Scales the tool along the X axis
         
*/
        
public function scaleXInteraction():void {
            
            
// get distortion offset vertical movement
            var distortH:Point 
= distortOffset(new Point(innerMouseLoc.x, innerInteractionStart.y), innerInteractionStart.x - innerRegistration.x);
            
            
// update the matrix for vertical scale
            _toolMatrix.a 
+= distortH.x;
            _toolMatrix.b 
+= distortH.y;
            completeInteraction(
true);
        }
        
        
/**
         
* Control Interaction.  Scales the tool along the Y axis
         
*/
        
public function scaleYInteraction():void {
            
// get distortion offset vertical movement
            var distortV:Point 
= distortOffset(new Point(innerInteractionStart.x, innerMouseLoc.y), innerInteractionStart.y - innerRegistration.y);
            
            
// update the matrix for vertical scale
            _toolMatrix.c 
+= distortV.x;
            _toolMatrix.d 
+= distortV.y;
            completeInteraction(
true);
        }
        
        
/**
         
* Control Interaction.  Scales the tool along both the X and Y axes
         
*/
        
public function scaleBothInteraction():void {
            
// mouse reference, may change from innerMouseLoc if constraining
            var innerMouseRef:Point 
= innerMouseLoc.clone();
            
            
if (_constrainScale) {
                
                
// how much the mouse has moved from starting the interaction
                var moved:Point 
= innerMouseLoc.subtract(innerInteractionStart);
                
                
// the relationship of the start location to the registration point
                var regOffset:Point 
= innerInteractionStart.subtract(innerRegistration);
                
                
// find the ratios between movement and the registration offset
                var ratioH 
= regOffset.x ? moved.x/regOffset.x : 0;
                var ratioV 
= regOffset.y ? moved.y/regOffset.y : 0;
                
                
// have the larger of the movement distances brought down
                
// based on the lowest ratio to fit the registration offset
                
if (ratioH > ratioV) {
                    innerMouseRef.x 
= innerInteractionStart.x + regOffset.x * ratioV;
                }
else{
                    innerMouseRef.y 
= innerInteractionStart.y + regOffset.y * ratioH;
                }
            }
            
            
// get distortion offsets for both vertical and horizontal movements
            var distortH:Point 
= distortOffset(new Point(innerMouseRef.x, innerInteractionStart.y), innerInteractionStart.x - innerRegistration.x);
            var distortV:Point 
= distortOffset(new Point(innerInteractionStart.x, innerMouseRef.y), innerInteractionStart.y - innerRegistration.y);
            
            
// update the matrix for both scales
            _toolMatrix.a 
+= distortH.x;
            _toolMatrix.b 
+= distortH.y;
            _toolMatrix.c 
+= distortV.x;
            _toolMatrix.d 
+= distortV.y;
            completeInteraction(
true);
        }
        
        
/**
         
* Control Interaction.  Skews the tool along the X axis
         
*/
        
public function skewXInteraction():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():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 {
            
// get changes in matrix combinations based on targetBounds
            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) {
                
// offset of registration to have transformations based around
                
// custom registration point
                var offset:Point 
= _registration.subtract(_toolMatrix.transformPoint(innerRegistration));
                _toolMatrix.tx 
+= offset.x;
                _toolMatrix.ty 
+= offset.y;
            }
            updateBounds();
        }
        
        
// Information
        
private function distortAngle():Number {
            
// use global mouse and registration
            var globalMatrix:Matrix 
= transform.concatenatedMatrix;
            var gMouseLoc:Point 
= globalMatrix.transformPoint(mouseLoc);
            var gRegistration:Point 
= globalMatrix.transformPoint(_registration);
            
            
// distance and angle of mouse from registration
            var offset:Point 
= gMouseLoc.subtract(gRegistration);
            return Math.atan2(offset.y, offset.x);
        }
        
        
// Updates
        
private function updateMouse():void {
            mouseLoc 
= new Point(mouseX, mouseY).add(mouseOffset);
            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) {
                    
// counter transform of the parents of the tool
                    var current:Matrix 
= transform.concatenatedMatrix;
                    current.invert();
                    _toolMatrix.concat(current);
                }
                enforceLimits();
                toolInvertedMatrix 
= _toolMatrix.clone();
                toolInvertedMatrix.invert();
                updateBounds();
            }
        }
        
        
private function updateBounds():void {
            
if (_target) {
                
// update tool bounds based on target bounds
                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 {
            
// show toolSprites only if there is a valid target
            var isChild:
Boolean = contains(toolSprites);
            
if (_target) {
                
if (!isChild) {
                    addChild(toolSprites);
                }                
            }
else if (isChild) {
                removeChild(toolSprites);
            }
        }
        
        
private function updateControlsEnabled():void {
            
// highest arrangement
            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);
            
// lowest arrangement
        }
        
        
private function updateControlContainer(container:Sprite, enabled:Boolean):void {
            var isChild:
Boolean = toolSprites.contains(container);
            
if (enabled) {
                
// add child or sent to bottom if enabled
                
if (isChild) {
                    toolSprites.setChildIndex(container, 
0);
                }
else{
                    toolSprites.addChildAt(container, 
0);
                }
            }
else if (isChild) {
                
// removed if disabled
                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;
            
            
// use global matrix
            var _globalMatrix:Matrix 
= _toolMatrix.clone();
            _globalMatrix.concat(transform.concatenatedMatrix);
            
            
// check current scale in X
            currScale 
= Math.sqrt(_globalMatrix.a * _globalMatrix.a + _globalMatrix.b * _globalMatrix.b);
            
if (currScale > _maxScaleX) {
                
// set scaleX to no greater than _maxScaleX
                angle 
= Math.atan2(_globalMatrix.b, _globalMatrix.a);
                _globalMatrix.a 
= Math.cos(angle) * _maxScaleX;
                _globalMatrix.b 
= Math.sin(angle) * _maxScaleX;
                enforced 
= true;
            }
            
            
// check current scale in Y
            currScale 
= Math.sqrt(_globalMatrix.c * _globalMatrix.c + _globalMatrix.d * _globalMatrix.d);
            
if (currScale > _maxScaleY) {
                
// set scaleY to no greater than _maxScaleY
                angle
= Math.atan2(_globalMatrix.c, _globalMatrix.d);
                _globalMatrix.d 
= Math.cos(angle) * _maxScaleY;
                _globalMatrix.c 
= Math.sin(angle) * _maxScaleY;
                enforced 
= true;
            }
            
            
            
// if scale was enforced, apply to _toolMatrix
            
if (enforced) {
                _toolMatrix 
= _globalMatrix;
                var current:Matrix 
= transform.concatenatedMatrix;
                current.invert();
                _toolMatrix.concat(current);
            }
        }
        
        
// Render
        
private function setNewRegistation():void {
            
if (_rememberRegistration && _target in registrationLog) {
                
                
// retrieved saved reg point in log
                var savedReg:Point 
= registrationLog[_target];
                innerRegistration 
= registrationLog[_target];
            }
else{
                
                
// use internal own point
                innerRegistration 
= new Point(00);
            }
            updateRegistration();
        }
        
        
private function raiseTarget():void {
            
// set target to last object in display list
            var index:
int = _target.parent.numChildren - 1;
            _target.parent.setChildIndex(_target, index);
            
            
// if this tool is in the same display list
            
// raise it to the top above target
            
if (_target.parent == parent) {
                parent.setChildIndex(this, index);
            }
        }
        
        
/**
         
* Draws the transform tool over its target instance
         
*/
        
public function draw():void {
            
// update the matrix and draw controls
            updateMatrix();
            dispatchEvent(
new Event(TRANSFORM_TOOL));
        }
        
        
/**
         
* Applies the current tool transformation to its target instance
         
*/
        
public function apply():void {
            
if (_target) {
                
                
// get matrix to apply to target
                var applyMatrix:Matrix 
= _toolMatrix.clone();
                applyMatrix.concat(transform.concatenatedMatrix);
                
                
// if target has a parent, counter parent transformations
                
if (_target.parent) {
                    var invertMatrix:Matrix 
= target.parent.transform.concatenatedMatrix;
                    invertMatrix.invert();
                    applyMatrix.concat(invertMatrix);
                }
                
                
// set target's matrix
                _target.transform.matrix = applyMatrix;
                
                dispatchEvent(
new Event(TRANSFORM_TARGET));
            }
        }
    }
}


import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;

import com.senocular.display.TransformTool;
import com.senocular.display.TransformToolControl;
import com.senocular.display.TransformToolCursor;

// Controls
class TransformToolInternalControl extends TransformToolControl {
    
    
public var interactionMethod:Function;
    
public var referenceName:String;
    
public var _skin:DisplayObject;
    
    
public function set skin(skin:DisplayObject):void {
        
if (_skin && contains(_skin)) {
            removeChild(_skin);
        }
        _skin 
= skin;
        
if (_skin) {
            addChild(_skin);
        }
        draw();
    }
    
    
public function get skin():DisplayObject {
        return _skin;
    }
    
    override 
public function get referencePoint():Point {
        
if (referenceName in _transformTool) {
            return _transformTool[referenceName];
        }
        return 
null;
    }
        
    
/*
     
* Constructor
     
*/    
    
public function TransformToolInternalControl(name:String, interactionMethod:Function = null, referenceName:String = null) {
        this.name 
= name;
        this.interactionMethod 
= interactionMethod;
        this.referenceName 
= referenceName;
        addEventListener(TransformTool.CONTROL_INIT, init);
    }
    
    protected 
function init(event:Event):void {
        _transformTool.addEventListener(TransformTool.NEW_TARGET, draw);
        _transformTool.addEventListener(TransformTool.TRANSFORM_TOOL, draw);
        _transformTool.addEventListener(TransformTool.CONTROL_TRANSFORM_TOOL, position);
        _transformTool.addEventListener(TransformTool.CONTROL_PREFERENCE, draw);
        _transformTool.addEventListener(TransformTool.CONTROL_MOVE, controlMove);
        draw();
    }
    
    
public function draw(event:Event = null):void {
        
if (_transformTool.maintainControlForm) {
            counterTransform();
        }
        position();
    }
    
    
public function position(event:Event = null):void {
        var reference:Point 
= referencePoint;
        
if (reference) {
            x 
= reference.x;
            y 
= reference.y;
        }
    }
    
    
private function controlMove(event:Event):void {
        
if ((interactionMethod is Function&& (_transformTool.currentControl == this)) {
            interactionMethod();
        }
    }
}


class TransformToolMoveShape extends TransformToolInternalControl {
    
    
private var lastTarget:DisplayObject;
    
    
function TransformToolMoveShape(name:String, interactionMethod:Function) {
        super(name, interactionMethod);
    }
    
    override 
public function draw(event:Event = null):void {
        
        var currTarget:DisplayObject;
        var moveUnderObjects:
Boolean = _transformTool.moveUnderObjects;
        
        
// use hitArea if moving under objects
        
// then movement would have the same depth as the tool
        
if (moveUnderObjects) {
            hitArea 
= _transformTool.target as Sprite;
            currTarget 
= null;
            relatedObject 
= this;
            
        }
else{
            
            
// when not moving under objects
            
// use the tool target to handle movement allowing
            
// objects above it to be selectable
            hitArea 
= null;
            currTarget 
= _transformTool.target;
            relatedObject 
= _transformTool.target as InteractiveObject;
        }
        
        
if (lastTarget != currTarget) {
            
// set up/remove listeners for target being clicked
            
if (lastTarget) {
                lastTarget.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown, 
false);
            }
            
if (currTarget) {
                currTarget.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown, 
false0true);
            }
            
            
// register/unregister cursor with the object
            var cursor:TransformToolCursor 
= _transformTool.moveCursor;
            cursor.removeReference(lastTarget);
            cursor.addReference(currTarget);
            
            lastTarget 
= currTarget;
        }
    }
    
    
private function mouseDown(event:MouseEvent):void {
        dispatchEvent(
new MouseEvent(MouseEvent.MOUSE_DOWN));
    }
}


class TransformToolRegistrationControl extends TransformToolInternalControl {
        
    
function TransformToolRegistrationControl(name:String, interactionMethod:Function, referenceName:String) {
        super(name, interactionMethod, referenceName);
    }

    override 
public function draw(event:Event = null):void {
        graphics.clear();
        
if (!_skin) {
            graphics.lineStyle(
10);
            graphics.beginFill(0xFFFFFF);
            graphics.drawCircle(
00, _transformTool.controlSize/2);
            graphics.endFill();
        }
        super.draw();
    }
}


class TransformToolScaleControl extends TransformToolInternalControl {
    
    
function TransformToolScaleControl(name:String, interactionMethod:Function, referenceName:String) {
        super(name, interactionMethod, referenceName);
    }

    override 
public function draw(event:Event = null):void {
        graphics.clear();
        
if (!_skin) {
            graphics.lineStyle(
2, 0xFFFFFF);
            graphics.beginFill(
0);
            var size 
= _transformTool.controlSize;
            var size2:Number 
= size/2;
            graphics.drawRect(
-size2, -size2, size, size);
            graphics.endFill();
        }
        super.draw();
    }
}


class TransformToolRotateControl extends TransformToolInternalControl {
    
    
private var locationName:String;
    
    
function TransformToolRotateControl(name:String, interactionMethod:Function, locationName:String) {
        super(name, interactionMethod);
        this.locationName 
= locationName;
    }

    override 
public function draw(event:Event = null):void {
        graphics.clear();
        
if (!_skin) {
            graphics.beginFill(0xFF, 
0);
            graphics.drawCircle(
00, _transformTool.controlSize*2);
            graphics.endFill();
        }
        super.draw();
    }
    
    override 
public function position(event:Event = null):void {
        
if (locationName in _transformTool) {
            var location:Point 
= _transformTool[locationName];
            x 
= location.x;
            y 
= location.y;
        }
    }
}


class TransformToolSkewBar extends TransformToolInternalControl {
    
    
private var locationStart:String;
    
private var locationEnd:String;
    
    
function TransformToolSkewBar(name:String, interactionMethod:Function, referenceName:String, locationStart:String, locationEnd:String) {
        super(name, interactionMethod, referenceName);
        this.locationStart 
= locationStart;
        this.locationEnd 
= locationEnd;
    }
    
    override 
public function draw(event:Event = null):void {
        graphics.clear();
        
        
if (_skin) {
            super.draw(event);
            return;
        }
        
        
// derive point locations for bar
        var locStart:Point 
= _transformTool[locationStart];
        var locEnd:Point 
= _transformTool[locationEnd];
        
        
// counter transform
        var toolTrans:Matrix;
        var toolTransInverted:Matrix;
        var maintainControlForm:
Boolean = _transformTool.maintainControlForm;
        
if (maintainControlForm) {
            toolTrans 
= transform.concatenatedMatrix;
            toolTransInverted 
= toolTrans.clone();
            toolTransInverted.invert();
            
            locStart 
= toolTrans.transformPoint(locStart);
            locEnd 
= toolTrans.transformPoint(locEnd);
        }
        
        var size:Number 
= _transformTool.controlSize/2;
        var diff:Point 
= locEnd.subtract(locStart);
        var angle:Number 
= Math.atan2(diff.y, diff.x) - Math.PI/2;    
        var offset:Point 
= Point.polar(size, angle);
        
        var corner1:Point 
= locStart.add(offset);
        var corner2:Point 
= locEnd.add(offset);
        var corner3:Point 
= locEnd.subtract(offset);
        var corner4:Point 
= locStart.subtract(offset);
        
if (maintainControlForm) {
            corner1 
= toolTransInverted.transformPoint(corner1);
            corner2 
= toolTransInverted.transformPoint(corner2);
            corner3 
= toolTransInverted.transformPoint(corner3);
            corner4 
= toolTransInverted.transformPoint(corner4);
        }
        
        
// draw bar
        graphics.beginFill(0xFF0000, 
0);
        graphics.moveTo(corner1.x, corner1.y);
        graphics.lineTo(corner2.x, corner2.y);
        graphics.lineTo(corner3.x, corner3.y);
        graphics.lineTo(corner4.x, corner4.y);
        graphics.lineTo(corner1.x, corner1.y);
        graphics.endFill();
    }

    override 
public function position(event:Event = null):void {
        
if (_skin) {
            var locStart:Point 
= _transformTool[locationStart];
            var locEnd:Point 
= _transformTool[locationEnd];
            var location:Point 
= Point.interpolate(locStart, locEnd, .5);
            x 
= location.x;
            y 
= location.y;
        }
else{
            x 
= 0;
            y 
= 0;
            draw(event);
        }
    }
}


class TransformToolOutline extends TransformToolInternalControl {
    
    
function TransformToolOutline(name:String) {
        super(name);
    }

    override 
public function draw(event:Event = null):void {
        var topLeft:Point 
= _transformTool.boundsTopLeft;
        var topRight:Point 
= _transformTool.boundsTopRight;
        var bottomRight:Point 
= _transformTool.boundsBottomRight;
        var bottomLeft:Point 
= _transformTool.boundsBottomLeft;
        
        graphics.clear();
        graphics.lineStyle(
00);
        graphics.moveTo(topLeft.x, topLeft.y);
        graphics.lineTo(topRight.x, topRight.y);
        graphics.lineTo(bottomRight.x, bottomRight.y);
        graphics.lineTo(bottomLeft.x, bottomLeft.y);
        graphics.lineTo(topLeft.x, topLeft.y);
    }
    
    override 
public function position(event:Event = null):void {
        draw(event);
    }
}


// Cursors
class TransformToolInternalCursor extends TransformToolCursor {
    
    
public var offset:Point = new Point();
    
public var icon:Shape = new Shape();
    
    
public function TransformToolInternalCursor() {
        addChild(icon);
        offset 
= _mouseOffset;
        addEventListener(TransformTool.CONTROL_INIT, init);
    }
        
    
private function init(event:Event):void {
        _transformTool.addEventListener(TransformTool.NEW_TARGET, maintainTransform);
        _transformTool.addEventListener(TransformTool.CONTROL_PREFERENCE, maintainTransform);
        draw();
    }
    
    protected 
function maintainTransform(event:Event):void {
        offset 
= _mouseOffset;
        
if (_transformTool.maintainControlForm) {
            transform.matrix 
= new Matrix();
            var concatMatrix:Matrix 
= transform.concatenatedMatrix;
            concatMatrix.invert();
            transform.matrix 
= concatMatrix;
            offset 
= concatMatrix.deltaTransformPoint(offset);
        }
        updateVisible(event);
    }
    
    protected 
function drawArc(originX:Number, originY:Number, radius:Number, angle1:Number, angle2:Number, useMove:Boolean = true):void {
        var diff:Number 
= angle2 - angle1;
        var divs:Number 
= 1 + Math.floor(Math.abs(diff)/(Math.PI/4));
        var span:Number 
= diff/(2*divs);
        var cosSpan:Number 
= Math.cos(span);
        var radiusc:Number 
= cosSpan ? radius/cosSpan : 0;
        var i:
int;
        
if (useMove) {
            icon.graphics.moveTo(originX 
+ Math.cos(angle1)*radius, originY - Math.sin(angle1)*radius);
        }
else{
            icon.graphics.lineTo(originX 
+ Math.cos(angle1)*radius, originY - Math.sin(angle1)*radius);
        }
        
for (i=0; i<divs; i++) {
            angle2 
= angle1 + span;
            angle1 
= angle2 + span;
            icon.graphics.curveTo(
                originX 
+ Math.cos(angle2)*radiusc, originY - Math.sin(angle2)*radiusc,
                originX 
+ Math.cos(angle1)*radius, originY - Math.sin(angle1)*radius
            );
        }
    }
    
    protected 
function getGlobalAngle(vector:Point):Number {
        var globalMatrix:Matrix 
= _transformTool.globalMatrix;
        vector 
= globalMatrix.deltaTransformPoint(vector);
        return Math.atan2(vector.y, vector.x) 
* (180/Math.PI);
    }
    
    override 
public function position(event:Event = null):void {
        
if (parent) {
            x 
= parent.mouseX + offset.x;
            y 
= parent.mouseY + offset.y;
        }
    }
    
    
public function draw():void {
        icon.graphics.beginFill(
0);
        icon.graphics.lineStyle(
1, 0xFFFFFF);
    }
}


class TransformToolRegistrationCursor extends TransformToolInternalCursor {
    
    
public function TransformToolRegistrationCursor() {
    }
    
    override 
public function draw():void {
        super.draw();
        icon.graphics.drawCircle(
0,0,2);
        icon.graphics.drawCircle(
0,0,4);
        icon.graphics.endFill();
    }
}


class TransformToolMoveCursor extends TransformToolInternalCursor {
    
    
public function TransformToolMoveCursor() {
    }
    
    override 
public function draw():void {
        super.draw();
        
// up arrow
        icon.graphics.moveTo(
11);
        icon.graphics.lineTo(
1-2);
        icon.graphics.lineTo(
-1-2);
        icon.graphics.lineTo(
2-6);
        icon.graphics.lineTo(
5-2);
        icon.graphics.lineTo(
3-2);
        icon.graphics.lineTo(
31);
        
// right arrow
        icon.graphics.lineTo(
61);
        icon.graphics.lineTo(
6-1);
        icon.graphics.lineTo(
102);
        icon.graphics.lineTo(
65);
        icon.graphics.lineTo(
63);
        icon.graphics.lineTo(
33);
        
// down arrow
        icon.graphics.lineTo(
35);
        icon.graphics.lineTo(
36);
        icon.graphics.lineTo(
56);
        icon.graphics.lineTo(
210);
        icon.graphics.lineTo(
-16);
        icon.graphics.lineTo(
16);
        icon.graphics.lineTo(
15);
        
// left arrow
        icon.graphics.lineTo(
13);
        icon.graphics.lineTo(
-23);
        icon.graphics.lineTo(
-25);
        icon.graphics.lineTo(
-62);
        icon.graphics.lineTo(
-2-1);
        icon.graphics.lineTo(
-21);
        icon.graphics.lineTo(
11);
        icon.graphics.endFill();
    }
}


class TransformToolScaleCursor extends TransformToolInternalCursor {
    
    
public function TransformToolScaleCursor() {
    }
    
    override 
public function draw():void {
        super.draw();
        
// right arrow
        icon.graphics.moveTo(
4.5-0.5);
        icon.graphics.lineTo(
4.5-2.5);
        icon.graphics.lineTo(
8.50.5);
        icon.graphics.lineTo(
4.53.5);
        icon.graphics.lineTo(
4.51.5);
        icon.graphics.lineTo(
-0.51.5);
        
// left arrow
        icon.graphics.lineTo(
-3.51.5);
        icon.graphics.lineTo(
-3.53.5);
        icon.graphics.lineTo(
-7.50.5);
        icon.graphics.lineTo(
-3.5-2.5);
        icon.graphics.lineTo(
-3.5-0.5);
        icon.graphics.lineTo(
4.5-0.5);
        icon.graphics.endFill();
    }
    
    override 
public function updateVisible(event:Event = null):void {
        super.updateVisible(event);
        
if (event) {
            var reference:TransformToolScaleControl 
= event.target as TransformToolScaleControl;
            
if (reference) {
                switch(reference) {
                    
case _transformTool.scaleTopLeftControl:
                    
case _transformTool.scaleBottomRightControl:
                        icon.rotation 
= (getGlobalAngle(new Point(0,100)) + getGlobalAngle(new Point(100,0)))/2;
                        break;
                    
case _transformTool.scaleTopRightControl:
                    
case _transformTool.scaleBottomLeftControl:
                        icon.rotation 
= (getGlobalAngle(new Point(0,-100)) + getGlobalAngle(new Point(100,0)))/2;
                        break;
                    
case _transformTool.scaleTopControl:
                    
case _transformTool.scaleBottomControl:
                        icon.rotation 
= getGlobalAngle(new Point(0,100));
                        break;
                    default:
                        icon.rotation 
= getGlobalAngle(new Point(100,0));
                }
            }
        }
    }
}


class TransformToolRotateCursor extends TransformToolInternalCursor {
    
    
public function TransformToolRotateCursor() {
    }
    
    override 
public function draw():void {
        super.draw();
        
// curve
        var angle1:Number 
= Math.PI;
        var angle2:Number 
= -Math.PI/2;
        drawArc(
0,0,4, angle1, angle2);
        drawArc(
0,0,6, angle2, angle1, false);
        
// arrow
        icon.graphics.lineTo(
-80);
        icon.graphics.lineTo(
-54);
        icon.graphics.lineTo(
-20);
        icon.graphics.lineTo(
-40);
        icon.graphics.endFill();
    }
}


class TransformToolSkewCursor extends TransformToolInternalCursor {
    
    
public function TransformToolSkewCursor() {
    }
    
    override 
public function draw():void {
        super.draw();
        
// right arrow
        icon.graphics.moveTo(
-6-1);
        icon.graphics.lineTo(
6-1);
        icon.graphics.lineTo(
6-4);
        icon.graphics.lineTo(
101);
        icon.graphics.lineTo(
-61);
        icon.graphics.lineTo(
-6-1);
        icon.graphics.endFill();
        
        super.draw();
        
// left arrow
        icon.graphics.moveTo(
105);
        icon.graphics.lineTo(
-25);
        icon.graphics.lineTo(
-28);
        icon.graphics.lineTo(
-63);
        icon.graphics.lineTo(
103);
        icon.graphics.lineTo(
105);
        icon.graphics.endFill();
    }
    
    override 
public function updateVisible(event:Event = null):void {
        super.updateVisible(event);
        
if (event) {
            var reference:TransformToolSkewBar 
= event.target as TransformToolSkewBar;
            
if (reference) {
                switch(reference) {
                    
case _transformTool.skewLeftControl:
                    
case _transformTool.skewRightControl:
                        icon.rotation 
= getGlobalAngle(new Point(0,100));
                        break;
                    default:
                        icon.rotation 
= getGlobalAngle(new Point(100,0));
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/ddw1997/p/1579916.html