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 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():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 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;
}
// 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( 0 , 0 );
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( 0 , 0 );
}
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, false , 0 , true );
}
// 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( 1 , 0 );
graphics.beginFill(0xFFFFFF);
graphics.drawCircle( 0 , 0 , _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( 0 , 0 , _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( 0 , 0 );
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( 1 , 1 );
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( 3 , 1 );
// right arrow
icon.graphics.lineTo( 6 , 1 );
icon.graphics.lineTo( 6 , - 1 );
icon.graphics.lineTo( 10 , 2 );
icon.graphics.lineTo( 6 , 5 );
icon.graphics.lineTo( 6 , 3 );
icon.graphics.lineTo( 3 , 3 );
// down arrow
icon.graphics.lineTo( 3 , 5 );
icon.graphics.lineTo( 3 , 6 );
icon.graphics.lineTo( 5 , 6 );
icon.graphics.lineTo( 2 , 10 );
icon.graphics.lineTo( - 1 , 6 );
icon.graphics.lineTo( 1 , 6 );
icon.graphics.lineTo( 1 , 5 );
// left arrow
icon.graphics.lineTo( 1 , 3 );
icon.graphics.lineTo( - 2 , 3 );
icon.graphics.lineTo( - 2 , 5 );
icon.graphics.lineTo( - 6 , 2 );
icon.graphics.lineTo( - 2 , - 1 );
icon.graphics.lineTo( - 2 , 1 );
icon.graphics.lineTo( 1 , 1 );
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.5 , 0.5 );
icon.graphics.lineTo( 4.5 , 3.5 );
icon.graphics.lineTo( 4.5 , 1.5 );
icon.graphics.lineTo( - 0.5 , 1.5 );
// left arrow
icon.graphics.lineTo( - 3.5 , 1.5 );
icon.graphics.lineTo( - 3.5 , 3.5 );
icon.graphics.lineTo( - 7.5 , 0.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( - 8 , 0 );
icon.graphics.lineTo( - 5 , 4 );
icon.graphics.lineTo( - 2 , 0 );
icon.graphics.lineTo( - 4 , 0 );
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( 10 , 1 );
icon.graphics.lineTo( - 6 , 1 );
icon.graphics.lineTo( - 6 , - 1 );
icon.graphics.endFill();
super.draw();
// left arrow
icon.graphics.moveTo( 10 , 5 );
icon.graphics.lineTo( - 2 , 5 );
icon.graphics.lineTo( - 2 , 8 );
icon.graphics.lineTo( - 6 , 3 );
icon.graphics.lineTo( 10 , 3 );
icon.graphics.lineTo( 10 , 5 );
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 ));
}
}
}
}
}
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 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():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 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;
}
// 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( 0 , 0 );
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( 0 , 0 );
}
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, false , 0 , true );
}
// 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( 1 , 0 );
graphics.beginFill(0xFFFFFF);
graphics.drawCircle( 0 , 0 , _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( 0 , 0 , _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( 0 , 0 );
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( 1 , 1 );
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( 3 , 1 );
// right arrow
icon.graphics.lineTo( 6 , 1 );
icon.graphics.lineTo( 6 , - 1 );
icon.graphics.lineTo( 10 , 2 );
icon.graphics.lineTo( 6 , 5 );
icon.graphics.lineTo( 6 , 3 );
icon.graphics.lineTo( 3 , 3 );
// down arrow
icon.graphics.lineTo( 3 , 5 );
icon.graphics.lineTo( 3 , 6 );
icon.graphics.lineTo( 5 , 6 );
icon.graphics.lineTo( 2 , 10 );
icon.graphics.lineTo( - 1 , 6 );
icon.graphics.lineTo( 1 , 6 );
icon.graphics.lineTo( 1 , 5 );
// left arrow
icon.graphics.lineTo( 1 , 3 );
icon.graphics.lineTo( - 2 , 3 );
icon.graphics.lineTo( - 2 , 5 );
icon.graphics.lineTo( - 6 , 2 );
icon.graphics.lineTo( - 2 , - 1 );
icon.graphics.lineTo( - 2 , 1 );
icon.graphics.lineTo( 1 , 1 );
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.5 , 0.5 );
icon.graphics.lineTo( 4.5 , 3.5 );
icon.graphics.lineTo( 4.5 , 1.5 );
icon.graphics.lineTo( - 0.5 , 1.5 );
// left arrow
icon.graphics.lineTo( - 3.5 , 1.5 );
icon.graphics.lineTo( - 3.5 , 3.5 );
icon.graphics.lineTo( - 7.5 , 0.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( - 8 , 0 );
icon.graphics.lineTo( - 5 , 4 );
icon.graphics.lineTo( - 2 , 0 );
icon.graphics.lineTo( - 4 , 0 );
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( 10 , 1 );
icon.graphics.lineTo( - 6 , 1 );
icon.graphics.lineTo( - 6 , - 1 );
icon.graphics.endFill();
super.draw();
// left arrow
icon.graphics.moveTo( 10 , 5 );
icon.graphics.lineTo( - 2 , 5 );
icon.graphics.lineTo( - 2 , 8 );
icon.graphics.lineTo( - 6 , 3 );
icon.graphics.lineTo( 10 , 3 );
icon.graphics.lineTo( 10 , 5 );
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 ));
}
}
}
}
}