You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

973 lines
40 KiB
Plaintext

/**
*Handles are created on-the-fly for a figure. They are completelly managed by the HandleManager
*Each handle is responsable for an action. The {Handle} does not need to keep a reference to the parent shape
*as the HandleManager will do that.
*@constructor
*@this {Handle}
*@param {String} type - the type of handle
**/
function Handle(type){
/**Type of Handle*/
this.type = type;
/*These are stupidly initialized to 0 but they should not be present at all...
*anyway they got set to the proper values in HandleManager::handleGetAll() function*/
/**The center of the circle (x coordinates)*/
this.x = 0;
/**The center of the circle (y coordinates)*/
this.y = 0;
/**Used by Connector handles, to not display redundant handles (i.e. when they are on the same line)*/
this.visible = true;
}
/**It's a (static) vector of handle types
* Note: R - stand for rotation
* Note: More handles might be added in the future : like handles to increase the number of edges for a hexagone
* Those handles will be specific for a figure
**/
Handle.types = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'r' ]; //DO NOT CHANGE THE ORDER OF THESE VALUES
/**It's a (static) vector of connector types*/
Handle.connectorTypes = ['ns', 'ew'];
/**Creates a {Handle} out of JSON parsed object
*@param {JSONObject} o - the JSON parsed object
*@return {Handle} a newly constructed Handle
*@author Alex Gheorghiu <alex@scriptoid.com>
**/
Handle.load = function(o){
var newHandle = new Handle(o.type);
newHandle.x = o.x;
newHandle.y = o.y;
newHandle.visible = o.visible;
return newHandle;
}
/**Creates an array of handles from an array of {JSONObject}s
*@param {Array} v - the array of JSONObjects
*@return an {Array} of {Handle}s
*@author Alex Gheorghiu <alex@scriptoid.com>
**/
Handle.loadArray = function(v){
var newHandles = [];
for(var i=0; i< v.length; i++){
newHandles.push(Handle.load(v[i]));
}
return newHandles;
}
/**Default handle radius*/
Handle.RADIUS = 3;
Handle.prototype = {
constructor : Handle,
/**Compares to another Handle
*@param {Handle} group - - the other glue
*@return {Boolean} - true if equals, false otherwise
**/
equals : function(anotherHandle){
if(!anotherHandle instanceof Handle){
return false;
}
return this.type == anotherHandle.type
&& this.x == anotherHandle.x
&& this.y == anotherHandle.y
&& this.visible == anotherHandle.visible;
},
actionFigure : function(lastMove, newX, newY){
var m = this.actionShape(lastMove, newX, newY);
if(m[0] == 'rotate'){
var cmdRotate = new FigureRotateCommand(HandleManager.shape.id, m[1], m[2]);
cmdRotate.execute();
History.addUndo(cmdRotate);
} else if(m[0] == 'scale'){
var cmdScale = new FigureScaleCommand(HandleManager.shape.id, m[1], m[2]);
cmdScale.execute();
History.addUndo(cmdScale);
}
},
actionContainer : function(lastMove, newX, newY){
var m = this.actionShape(lastMove, newX, newY);
if(m[0] == 'rotate'){
//simply ingnore rotate
throw "Handles.js -> actionContainer -> rotate should be disabled for Container"
// var cmdRotate = new ContainerRotateCommand(HandleManager.shape.id, m[1], m[2]);
// cmdRotate.execute();
// History.addUndo(cmdRotate);
} else if(m[0] == 'scale'){
var cmdScale = new ContainerScaleCommand(HandleManager.shape.id, m[1], m[2]);
cmdScale.execute();
History.addUndo(cmdScale);
}
},
actionGroup : function(lastMove, newX, newY){
var m = this.actionShape(lastMove, newX, newY);
if(m[0] == 'rotate'){
var cmdRotate = new GroupRotateCommand(HandleManager.shape.id, m[1], m[2]);
cmdRotate.execute();
History.addUndo(cmdRotate);
} else if(m[0] == 'scale'){
var cmdScale = new GroupScaleCommand(HandleManager.shape.id, m[1], m[2]);
cmdScale.execute();
History.addUndo(cmdScale);
}
},
/**Handle actions for Figure
*
*@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
*@param {Number} newX - new X coordinate
*@param {Number} newY - new Y coordinate
*@return {Array} of two matrixes direct and revers
**/
actionShape: function(lastMove, newX, newY){
var matrixes = [];
var figBounds = HandleManager.shape.getBounds();
var transX = 0; //the amount of translation on Ox
var transY = 0; //the amount of translation on Oy
var scaleX = 1; //the scale percentage on Ox
var scaleY = 1; //the scale percentage on Oy
var arc = false;
//see if we have a resize and prepare the figure by moving it back to Origin and "unrotate" it
if(this.type == 'r'){
//rotationCoords[0] is always the center of the shape, we clone it as when we do -rotationCoords[0].x, it is set to 0.
var center = HandleManager.shape.rotationCoords[0].clone();
var endAngle = Util.getAngle(HandleManager.shape.rotationCoords[0],new Point(newX,newY));
var startAngle = Util.getAngle(HandleManager.shape.rotationCoords[0],HandleManager.shape.rotationCoords[1]);//new Point(lastMove[0],lastMove[1])
var rotAngle = endAngle - startAngle;
// HandleManager.shape.transform(Matrix.translationMatrix(-center.x, -center.y))
// HandleManager.shape.transform(Matrix.rotationMatrix(rotAngle))
// HandleManager.shape.transform(Matrix.translationMatrix(center.x, center.y));
var equivTransfMatrix = Matrix.mergeTransformations(
Matrix.translationMatrix(-center.x, -center.y),
Matrix.rotationMatrix(rotAngle),
Matrix.translationMatrix(center.x,center.y)
);
//TODO: make somehow to compute the inverse of it.
//@see http://en.wikipedia.org/wiki/Transformation_matrix#Rotation to find inverses
var inverseTransfMatrix = Matrix.mergeTransformations(
Matrix.translationMatrix(-center.x, -center.y),
Matrix.rotationMatrix(-rotAngle),
Matrix.translationMatrix(center.x,center.y)
);
matrixes = ['rotate', equivTransfMatrix, inverseTransfMatrix];
}
else{ //if not "rotate" (figure), "updown", "leftright" (connector)
//find the angle by which the figure was rotated (for any figure this is initally 0 so if != than 0 we have a rotation)
var angle = Util.getAngle(HandleManager.shape.rotationCoords[0], HandleManager.shape.rotationCoords[1]);
//save initial figure's center
var oldCenter = HandleManager.shape.rotationCoords[0].clone();
//move the new [x,y] to the "un-rotated" and "un-translated" space
var p = new Point(newX,newY);
p.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
p.transform(Matrix.rotationMatrix(-angle));
p.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
newX = p.x;
newY = p.y;
var handlerPoint=new Point(this.x,this.y) //Handler's center point (used to draw it's circle)
//rotate that as well.
handlerPoint.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
handlerPoint.transform(Matrix.rotationMatrix(-angle));
handlerPoint.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
switch(this.type){
case 'n':
/*move the xOy coodinates at the bottom of the figure and then scale*/
transY = figBounds[3];
if(newY < figBounds[3]-5){ //North must not get too close to South
scaleY = (figBounds[3]-newY)/(figBounds[3] - handlerPoint.y);
}
break;
case 's':
/*move the xOy coodinates at the top of the figure (superfluous as we are there already) and then scale*/
transY = figBounds[1];
if(newY > figBounds[1]+5){ //South must not get too close to North
scaleY = (newY-figBounds[1])/(handlerPoint.y-figBounds[1]);
}
break;
case 'w':
/*move the xOy coordinates at the right of the figure and then scale*/
transX = figBounds[2];
if(newX < figBounds[2]-5){ //West(newX) must not get too close to East(figBounds[2])
scaleX = (figBounds[2]-newX)/(figBounds[2]-handlerPoint.x);
}
break;
case 'e':
/*move the xOy coodinates at the left of the figure (superfluous as we are there already) and then scale*/
transX = figBounds[0];
if(newX > figBounds[0]+5){
scaleX = (newX-figBounds[0])/(handlerPoint.x-figBounds[0]);
}
break;
case 'nw':
/*You can think as a combined North and West action*/
transX = figBounds[2];
transY = figBounds[3];
if(newX<figBounds[2]-5 && newY<figBounds[3]-5){
scaleY = (figBounds[3]-newY) /(figBounds[3]-handlerPoint.y);
scaleX = (figBounds[2]-newX) / (figBounds[2]-handlerPoint.x);
}
break;
case 'ne':
transX = figBounds[0]
transY = figBounds[3];
if(newX>figBounds[0]+5 && newY<figBounds[3]-5){
scaleX = (newX-figBounds[0])/(handlerPoint.x-figBounds[0]);
scaleY = (figBounds[3]-newY)/(figBounds[3]-handlerPoint.y);
}
break;
case 'sw':
transX = figBounds[2]
transY = figBounds[1];
if(newX<figBounds[2]-5 && newY>figBounds[1]+5){
scaleX = (figBounds[2]-newX)/((figBounds[2]-handlerPoint.x));
scaleY = (newY-figBounds[1])/(handlerPoint.y-figBounds[1]);
}
break;
case 'se':
transX = figBounds[0];
transY = figBounds[1];
if(newX>figBounds[0]+5 && newY>figBounds[1]+5){
scaleY= (newY-figBounds[1]) / (handlerPoint.y-figBounds[1]);
scaleX= (newX-figBounds[0]) / (handlerPoint.x-figBounds[0]);
}
break;
}//end switch
/*By default the NW, NE, SW and SE are scalling keeping the ratio
*but you can use SHIFT to cause a free (no keep ratio) change
*So, if no SHIFT pressed we force a "keep ration" resize
**/
if(!SHIFT_PRESSED && transX != 0 && transY != 0){//keep ratios, only affects ne/nw resize
//if we are scaling along the x axis (West or East resize), with an arc(behaves like corner) then scale relative to x movement
//TODO: what's the reason for this?
if(this.getCursor()=="w-resize" || this.getCursor()=="e-resize"){
scaleY = scaleX;
}
else { //for everything else, scale based on y
scaleX = scaleY;
}
}
//move the figure to origine and "unrotate" it
var matrixToOrigin = Matrix.mergeTransformations(
Matrix.translationMatrix(-oldCenter.x,-oldCenter.y),
Matrix.rotationMatrix(-angle),
Matrix.translationMatrix(oldCenter.x,oldCenter.y)
);
// HandleManager.shape.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
// HandleManager.shape.transform(Matrix.rotationMatrix(-angle));
// HandleManager.shape.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
//
// //scale matrix
// HandleManager.shape.transform(Matrix.translationMatrix(-transX, -transY));
// HandleManager.shape.transform(Matrix.scaleMatrix(scaleX, scaleY))
// HandleManager.shape.transform(Matrix.translationMatrix(transX, transY));
//
//
// //move and rotate the figure back to its original coordinates
// HandleManager.shape.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
// HandleManager.shape.transform(Matrix.rotationMatrix(angle));
// HandleManager.shape.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
// HandleManager.shape.transform(directMatrix);
//scale matrix
var scaleMatrix = Matrix.mergeTransformations(
Matrix.translationMatrix(-transX, -transY),
Matrix.scaleMatrix(scaleX, scaleY),
Matrix.translationMatrix(transX, transY)
);
var unscaleMatrix = Matrix.mergeTransformations(
Matrix.translationMatrix(-transX, -transY),
Matrix.scaleMatrix(1/scaleX, 1/scaleY),
Matrix.translationMatrix(transX, transY)
);
//move and rotate the figure back to its original coordinates
var matrixBackFromOrigin = Matrix.mergeTransformations(
Matrix.translationMatrix(-oldCenter.x,-oldCenter.y),
Matrix.rotationMatrix(angle),
Matrix.translationMatrix(oldCenter.x,oldCenter.y)
);
var directMatrix = Matrix.mergeTransformations(matrixToOrigin, scaleMatrix, matrixBackFromOrigin);
var reverseMatrix = Matrix.mergeTransformations(matrixToOrigin, unscaleMatrix, matrixBackFromOrigin);
matrixes = ['scale', directMatrix, reverseMatrix];
} //end else
return matrixes;
},
/**
*Handle actions for Connector
*
*@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
*@param {Number} newX - new X coordinate
*@param {Number} newY - new Y coordinate
**/
actionConnector: function(lastMove, newX, newY){
switch(this.type){
case 'v':
var index;
//find the two turning points this handle is in between
for(var i = 1; i < HandleManager.shape.turningPoints.length-1; i++){
if(HandleManager.shape.turningPoints[i-1].y == HandleManager.shape.turningPoints[i].y
&& HandleManager.shape.turningPoints[i].y == this.y
&& Math.min(HandleManager.shape.turningPoints[i].x, HandleManager.shape.turningPoints[i-1].x) <= this.x
&& Math.max(HandleManager.shape.turningPoints[i].x, HandleManager.shape.turningPoints[i-1].x) >= this.x)
{
index = i;
}
}
var deltaY = newY - lastMove[1]; //Take changes on Oy
var translationMatrix = Matrix.translationMatrix(0, deltaY); //Generate translation matrix
/*TODO: make changes to DIAGRAMO.debugSolutions here
* because, otherwise, those changes are not reflected in debug painting of Connector
*/
//Pick turning points neighbours and translate them
HandleManager.shape.turningPoints[index-1].transform(translationMatrix);
HandleManager.shape.turningPoints[index].transform(translationMatrix);
// add new changes in {Connector}
HandleManager.shape.addUserChange({
align: Connector.USER_CHANGE_VERTICAL_ALIGN,
delta: deltaY,
index: index - 1
});
HandleManager.shape.addUserChange({
align: Connector.USER_CHANGE_VERTICAL_ALIGN,
delta: deltaY,
index: index
});
break;
case 'h':
var index;
//find the two turning points this handle is in between
for(var i = 1; i < HandleManager.shape.turningPoints.length-1; i++){
if(HandleManager.shape.turningPoints[i-1].x == HandleManager.shape.turningPoints[i].x
&& HandleManager.shape.turningPoints[i].x == this.x
&& Math.min(HandleManager.shape.turningPoints[i].y, HandleManager.shape.turningPoints[i-1].y) <= this.y
&& Math.max(HandleManager.shape.turningPoints[i].y, HandleManager.shape.turningPoints[i-1].y) >= this.y)
{
index = i;
}
}
var deltaX = newX-lastMove[0]; //Take changes on Ox
var translationMatrix = Matrix.translationMatrix(deltaX, 0); //Generate translation matrix
/*TODO: make changes to DIAGRAMO.debugSolution here
* because, otherwise, those changes are not reflected in debug painting of Connector
*/
//Pick turning points neighbours and translate them
HandleManager.shape.turningPoints[index-1].transform(translationMatrix);
HandleManager.shape.turningPoints[index].transform(translationMatrix);
// add new changes in {Connector}
HandleManager.shape.addUserChange({
align: Connector.USER_CHANGE_HORIZONTAL_ALIGN,
delta: deltaX,
index: index - 1
});
HandleManager.shape.addUserChange({
align: Connector.USER_CHANGE_HORIZONTAL_ALIGN,
delta: deltaX,
index: index
});
break;
}
HandleManager.shape.updateMiddleText();
},
/**Handle an action. Simply dispatch to the correct handler
*@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
*@param {Number} newX - new X coordinate
*@param {Number} newY - new Y coordinate
**/
action: function(lastMove, newX, newY){
if(lastMove == null || lastMove.length != 2){
throw 'Handle:action() Last move is wrong';
}
if(HandleManager.shape instanceof Connector){
this.actionConnector(lastMove, newX, newY);
}
else if(HandleManager.shape instanceof Figure){
this.actionFigure(lastMove, newX, newY);
}
else if(HandleManager.shape instanceof Group){
this.actionGroup(lastMove, newX, newY);
}
else if(HandleManager.shape instanceof Container){
this.actionContainer(lastMove, newX, newY);
}
},
/**This is the method you have to call to paint a handler
* All handles will be circles...so we avoid to much of the computing for rectangle handles
* They will have a filling color (green) and a stoke (black)
* @param {Context} context - the 2D context
**/
paint : function(context){
context.save();
//fill the handler
context.beginPath();
context.arc(this.x, this.y, Handle.RADIUS, 0, Math.PI*2, false);
context.fillStyle = "rgb(0,255,0)";
context.closePath();
context.fill();
//stroke the handler
context.beginPath();
context.arc(this.x, this.y, Handle.RADIUS, 0, Math.PI*2, false);
context.strokeStyle = "rgb(0,0,0)";
context.lineWidth = defaultThinLineWidth;
context.closePath();
context.stroke();
// if(this.type == 'r'){
// var line = new Line(new Point(this.x,this.y), new Point(HandleManager.handles[1].x,HandleManager.handles[1].y));
// line.style.dashLength = 3;
// line.style.strokeStyle = "grey";
// line.style.lineWidth = defaultThinLineWidth;
// line.paint(context);
// }
// context.restore();
},
/**See if the handle contains a point
*@param {Number} x - the x coordinate of the point
*@param {Number} y - the y coordinate of the point
**/
contains:function(x,y){
var p=new Point(this.x,this.y);
return p.near(x,y, Handle.RADIUS);
},
/**
*Get a handle bounds
**/
getBounds : function(){
return [this.x - Handle.RADIUS, this.y - Handle.RADIUS, this.x + Handle.RADIUS,this.y + Handle.RADIUS];
},
/**
*Transform the Handle through a matrix
*@param {Matrix} matrix - the matrix that will perform the transformation
**/
transform: function(matrix){
var p=new Point(this.x,this.y)
p.transform(matrix);
this.x=p.x;
this.y=p.y;
},
/**Get the specific cursor for this handle. Cursor is ONLY a visual clue for
* the user to know how to move his mouse.
*
*Behaviour:
* If North handle is in the North we have 'Up/Down arrow" cursor
* If North handle is in the West (so it has "Left/Right arrow") (or anything different that North)
* we have 'Left/Right arrow' cursor but the figure will expand as follows:
* - rotate back to initial position
* - expand North
* - rotate back to current position
* - repaint
* @see <a href="http://www.w3schools.com/css/pr_class_cursor.asp">http://www.w3schools.com/css/pr_class_cursor.asp</a> for cusor values
* @author Zack Newsham <zack_newsham@yahoo.co.uk>
* @author Alex Gheorghiu <alex@scriptoid.com>
**/
getCursor:function(){
if(HandleManager.shape instanceof Connector){
if(this.visible == false){
return "";
}
if(this.type == 'v'){
return 'ns-resize';
}
else{
return 'ew-resize';
}
} //end if Connector
else if(HandleManager.shape instanceof Figure ){
if(this.visible == false){
return "";
}
if(this.type == 'r'){
return 'move';
}
var figureBounds = HandleManager.shape.getBounds(); //get figure's bounds
var figureCenter = new Point(figureBounds[0] + ((figureBounds[2]-figureBounds[0])/2),
(figureBounds[1] + ((figureBounds[3] - figureBounds[1])/2)) ); //get figure's center
//find north
var closestToNorthIndex = -1; //keeps the index of closest handle to North
var minAngleToNorth = 2 * Math.PI; //keeps the smallest (angular) distante to North
var myIndex = -1;
for(var i=0; i<HandleManager.handles.length-1; i++){
var handleCenter = new Point(HandleManager.handles[i].x, HandleManager.handles[i].y);
var angle = Util.getAngle(figureCenter,handleCenter); //get the angle between those 2 points 0=n
if(angle <= Math.PI){ //quadrant I or II
if(angle < minAngleToNorth){
minAngleToNorth = angle;
closestToNorthIndex = i;
}
}
else{ //quadrant III or IV
if(2 * Math.PI - angle < minAngleToNorth){
minAngleToNorth = 2 * Math.PI - angle
closestToNorthIndex = i;
}
}
}
//alert("closest to North is : " + closestToNorthIndex);
for(var k=0; k<8; k++){ //there will always be 8 resize handlers
//we do not use modulo 9 as we want to ignore the "rotate" handle
if(HandleManager.handles[(closestToNorthIndex + k) % 8] == this){
return Handle.types[k]+"-resize";
}
}
//end if Figure
} else if(HandleManager.shape instanceof Container){
if(this.visible == false){
return "";
}
if(this.type == 'r'){
return 'move';
}
var figureBounds = HandleManager.shape.getBounds(); //get figure's bounds
var figureCenter = new Point(figureBounds[0] + ((figureBounds[2]-figureBounds[0])/2),
(figureBounds[1] + ((figureBounds[3] - figureBounds[1])/2)) ); //get figure's center
//find north
var closestToNorthIndex = -1; //keeps the index of closest handle to North
var minAngleToNorth = 2 * Math.PI; //keeps the smallest (angular) distante to North
var myIndex = -1;
for(var i=0; i<HandleManager.handles.length-1; i++){
var handleCenter = new Point(HandleManager.handles[i].x, HandleManager.handles[i].y);
var angle = Util.getAngle(figureCenter,handleCenter); //get the angle between those 2 points 0=n
if(angle <= Math.PI){ //quadrant I or II
if(angle < minAngleToNorth){
minAngleToNorth = angle;
closestToNorthIndex = i;
}
}
else{ //quadrant III or IV
if(2 * Math.PI - angle < minAngleToNorth){
minAngleToNorth = 2 * Math.PI - angle
closestToNorthIndex = i;
}
}
}
//alert("closest to North is : " + closestToNorthIndex);
for(var k=0; k<8; k++){ //there will always be 8 resize handlers
//we do not use modulo 9 as we want to ignore the "rotate" handle
if(HandleManager.handles[(closestToNorthIndex + k) % 8] == this){
return Handle.types[k]+"-resize";
}
}
//end if Figure
} else if(HandleManager.shape instanceof Group){
if(this.visible == false){
return "";
}
// only rotate handle enabled for a group
if(this.type == 'r'){
return 'move';
}
//end if Group
}
return "";
}
}
/**HandleManager will act like a Singleton (even not defined as one)
* You will attach a Figure to it and he will be in charge with the figure manipulation
* @constructor
* @this {HandleManager}
**/
function HandleManager(){
}
/**The shape (figure or connector) that the HandleManager will manage*/
HandleManager.shape = null;
/**An {Array} with current handles*/
HandleManager.handles = [];
/**An {Array} with connector handles*/
HandleManager.connectorHandles = [];
/**Selection rectangle (the rectangle upon the Handles will stay in case of a Figure/Group)*/
HandleManager.selectRect = null;
/**Currently selected handle*/
HandleManager.handleSelectedIndex = -1;
/**Distance from shape where to draw the handles*/
HandleManager.handleOffset = 0;//JS: I want handles to be on figure corners, because then we can correctly see figure with no stroke (was 10)
/**Get selected handle or null if no handler selected*/
HandleManager.handleGetSelected = function(){
if(HandleManager.handleSelectedIndex!=-1){
return HandleManager.handles[HandleManager.handleSelectedIndex];
}
return null;
}
/**Use this method to set a new shape (Figure or Connetor) to this manager.
* Every time a new figure is set, old handles will dissapear (got erased by new figure's handles)
**/
HandleManager.shapeSet = function (shape) {
HandleManager.shape = shape;
//1. clear old/previous handles
HandleManager.handles = [];
//2. setup/add handles for this figure
if (shape instanceof Connector) {
HandleManager.selectRect = null;
//we don't want to affect the start or end points
for (var i = 1; i < shape.turningPoints.length - 2; i++) {
var h;
/*
*Create a new handle ONLY if previous, current and next turning points are not colinear
**/
if (
/*Previous points are not collinear and next points are either
* non colinear or last 2 coincide (this case appears when dragging).
*
* Basically ensure the segment [i, i+1] is not on same line with
* [i-1, i] or [i+1, i+2]*/
(
!Util.collinearity(HandleManager.shape.turningPoints[i - 1], HandleManager.shape.turningPoints[i], HandleManager.shape.turningPoints[i + 1])
&&
(!Util.collinearity(HandleManager.shape.turningPoints[i], HandleManager.shape.turningPoints[i + 1], HandleManager.shape.turningPoints[i + 2])
|| HandleManager.shape.turningPoints[i + 1].equals(HandleManager.shape.turningPoints[i + 2]) /*Next two coincide? TODO: Do we really need this? Explain*/
)
)
||
/*Previous points are non colinear or first 2 of them coincide and next points are not colinear*/
(
(!Util.collinearity(HandleManager.shape.turningPoints[i - 1], HandleManager.shape.turningPoints[i], HandleManager.shape.turningPoints[i + 1])
|| HandleManager.shape.turningPoints[i - 1].equals(HandleManager.shape.turningPoints[i]) /*Previous two coincide? TODO: Do we really need this? Explain*/)
&&
!Util.collinearity(HandleManager.shape.turningPoints[i], HandleManager.shape.turningPoints[i + 1], HandleManager.shape.turningPoints[i + 2])
)
) {
if (shape.turningPoints[i].x === shape.turningPoints[i + 1].x) { //same vertical
h = new Handle("h");
h.x = HandleManager.shape.turningPoints[i].x;
h.y = (HandleManager.shape.turningPoints[i].y + HandleManager.shape.turningPoints[i + 1].y) / 2;
}
else if (shape.turningPoints[i].y === shape.turningPoints[i + 1].y) { // same horizontal
h = new Handle("v");
h.x = (HandleManager.shape.turningPoints[i].x + HandleManager.shape.turningPoints[i + 1].x) / 2;
h.y = HandleManager.shape.turningPoints[i].y;
}
alert(h);
if (h) { // Did we created a Handle?
h.visible = true; // make it visible
HandleManager.handles.push(h); // add it to HandleManager
}
}
}
}
else if (shape instanceof Figure || shape instanceof Group) {
//find Figure's angle
var angle = Util.getAngle(HandleManager.shape.rotationCoords[0], HandleManager.shape.rotationCoords[1]);
//rotate it back to "normal" space (from current space)
HandleManager.shape.transform(Matrix.rotationMatrix(-angle), false);
HandleManager.selectRect = new Polygon();
//construct bounds of the Figure in "normal" space
var bounds = HandleManager.shape.getBounds();
HandleManager.selectRect.points = [];
HandleManager.selectRect.addPoint(new Point(bounds[0] - HandleManager.handleOffset, bounds[1] - HandleManager.handleOffset)); //top left
HandleManager.selectRect.addPoint(new Point(bounds[2] + HandleManager.handleOffset, bounds[1] - HandleManager.handleOffset)); //top right
HandleManager.selectRect.addPoint(new Point(bounds[2] + HandleManager.handleOffset, bounds[3] + HandleManager.handleOffset)); //bottom right
HandleManager.selectRect.addPoint(new Point(bounds[0] - HandleManager.handleOffset, bounds[3] + HandleManager.handleOffset)); //bottom left
bounds = HandleManager.selectRect.getBounds();
//update current handles
var handle = new Handle("nw"); //NW
handle.x = bounds[0];
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("n"); //N
handle.x = bounds[0] + (bounds[2] - bounds[0]) / 2;
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("ne"); //NE
handle.x = bounds[2];
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("e"); //E
handle.x = bounds[2];
handle.y = bounds[1] + (bounds[3] - bounds[1]) / 2;
HandleManager.handles.push(handle);
handle = new Handle("se"); //SE
handle.x = bounds[2];
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("s"); //S
handle.x = bounds[0] + (bounds[2] - bounds[0]) / 2;
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("sw"); //SW
handle.x = bounds[0];
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("w"); //W
handle.x = bounds[0];
handle.y = bounds[1] + (bounds[3] - bounds[1]) / 2;
HandleManager.handles.push(handle);
// handle = new Handle("r"); //Rotation
// handle.x = bounds[0]+(bounds[2]-bounds[0])/2;
// //JS: because handleOffset is 0, we still need to see rotation handle
// if (HandleManager.handleOffset!=0){
// handle.y = bounds[1] - HandleManager.handleOffset * 1.5;
// }else{
// handle.y = bounds[1] - 15;
// }
// HandleManager.handles.push(handle);
HandleManager.selectRect.transform(Matrix.rotationMatrix(angle));
//rotate figure from "normal" space to current space
HandleManager.shape.transform(Matrix.rotationMatrix(angle), false);
if (shape instanceof Figure) {
if (shape.primitives[0] instanceof Text && shape.primitives.length == 1) {
for (var i = 0; i < HandleManager.handles.length; i++) {
HandleManager.handles[i].visible = false;
}
}
}
//now transform the handles from "normal" space too
for (var i = 0; i < HandleManager.handles.length; i++) {
HandleManager.handles[i].transform(Matrix.rotationMatrix(angle));
}
} else if (shape instanceof Container) {
var bounds = HandleManager.shape.getBounds();
HandleManager.selectRect = new Polygon();
HandleManager.selectRect.points = [];
HandleManager.selectRect.addPoint(new Point(bounds[0] - HandleManager.handleOffset, bounds[1] - HandleManager.handleOffset)); //top left
HandleManager.selectRect.addPoint(new Point(bounds[2] + HandleManager.handleOffset, bounds[1] - HandleManager.handleOffset)); //top right
HandleManager.selectRect.addPoint(new Point(bounds[2] + HandleManager.handleOffset, bounds[3] + HandleManager.handleOffset)); //bottom right
HandleManager.selectRect.addPoint(new Point(bounds[0] - HandleManager.handleOffset, bounds[3] + HandleManager.handleOffset)); //bottom left
bounds = HandleManager.selectRect.getBounds();
//update current handles
var handle = new Handle("nw"); //NW
handle.x = bounds[0];
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("n"); //N
handle.x = bounds[0] + (bounds[2] - bounds[0]) / 2;
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("ne"); //NE
handle.x = bounds[2];
handle.y = bounds[1];
HandleManager.handles.push(handle);
handle = new Handle("e"); //E
handle.x = bounds[2];
handle.y = bounds[1] + (bounds[3] - bounds[1]) / 2;
HandleManager.handles.push(handle);
handle = new Handle("se"); //SE
handle.x = bounds[2];
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("s"); //S
handle.x = bounds[0] + (bounds[2] - bounds[0]) / 2;
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("sw"); //SW
handle.x = bounds[0];
handle.y = bounds[3];
HandleManager.handles.push(handle);
handle = new Handle("w"); //W
handle.x = bounds[0];
handle.y = bounds[1] + (bounds[3] - bounds[1]) / 2;
HandleManager.handles.push(handle);
// handle = new Handle("r"); //Rotation
// handle.x = bounds[0]+(bounds[2]-bounds[0])/2;
// //JS: because handleOffset is 0, we still need to see rotation handle
// if (HandleManager.handleOffset!=0){
// handle.y = bounds[1] - HandleManager.handleOffset * 1.5;
// }else{
// handle.y = bounds[1] - 15;
// }
// HandleManager.handles.push(handle);
}
}
/**Returns all handles for a shape (figure or connector).
*It does not mean that the HandleManager keeps records of all Handles for a
*Figure but more likely they are computed on-the-fly
*@return an {Array} of {Handle} that you can further use to manage the figure
**/
HandleManager.handleGetAll = function(){
return HandleManager.handles;
}
/**Returns the handle from a certain coordinates
*@param {Number} x - the value on Ox
*@param {Number} y - the value on Oy
***/
HandleManager.handleGet = function(x,y){
for(var i=0; i<HandleManager.handles.length; i++){
if(HandleManager.handles[i].contains(x,y)){
return HandleManager.handles[i];
}
}
return null;
}
/**
*Select the handle from a certain coordinates
*@param {Number} x - the value on Ox
*@param {Number} y - the value on Oy
**/
HandleManager.handleSelectXY = function(x,y){
HandleManager.handleSelectedIndex=-1;
for (var i=0; i<HandleManager.handles.length; i++){
if(HandleManager.handles[i].contains(x,y)){
HandleManager.handleSelectedIndex = i;
}
}
}
/**
*Clear HandleManager.
**/
HandleManager.clear = function(){
HandleManager.handleSelectedIndex = -1;
HandleManager.shape = null;
HandleManager.handles = [];
}
/**Paint the Handles, actually the HandleManager will delegate each paint to
*the proper Handle to paint
*@param {Context} context - the 2D context
**/
HandleManager.paint = function(context){
var handles = HandleManager.handleGetAll(); //calling this sets the coordinates
//paint first the selection rectangle
context.save();
//paint selection rectangle (if present - only for Figure and Group)
if(HandleManager.selectRect != null){
//alert("Handle manager paint!");
HandleManager.selectRect.style.strokeStyle = "grey";
HandleManager.selectRect.style.lineWidth = defaultThinLineWidth;
HandleManager.selectRect.paint(context);
}
//now paint handles
for(var i=0; i<handles.length; i++){
if(handles[i].visible == true){
handles[i].paint(context);
}
}
context.restore()
}