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.
1120 lines
37 KiB
Plaintext
1120 lines
37 KiB
Plaintext
/**
|
|
*Stack holds all the figures on the screen
|
|
*It will also hold the groups formed on the screen
|
|
*@this {Stack}
|
|
*@constructor
|
|
**/
|
|
function Stack(){
|
|
/**Keeps all the figures on the canvas*/
|
|
this.figures = [];
|
|
|
|
/**Keeps all the groups in the canvas*/
|
|
this.groups = [];
|
|
|
|
this.containers = [];
|
|
|
|
/**Keeps current generated Id. Not for direct access*/
|
|
this.currentId = 0;
|
|
|
|
/**Keeps a map like (figure Id, figure index). It is similar to an index*/
|
|
this.idToIndex = [];
|
|
|
|
/**Type used in serialization*/
|
|
this.oType = 'Stack';
|
|
}
|
|
|
|
|
|
|
|
/**Creates a {Stack} out of JSON parsed object
|
|
*@param {JSONObject} o - the JSON parsed object
|
|
*@return {Stack} a newly constructed Stack
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
Stack.load = function(o){
|
|
var newStack = new Stack(); //empty constructor
|
|
|
|
|
|
newStack.figures = Figure.loadArray(o.figures);
|
|
newStack.containers = Container.loadArray(o.containers);
|
|
newStack.groups = Group.loadArray(o.groups);
|
|
newStack.figureSelectedIndex = o.figureSelectedIndex;
|
|
newStack.currentId = o.currentId;
|
|
newStack.idToIndex = o.idToIndex;
|
|
|
|
return newStack;
|
|
}
|
|
|
|
|
|
Stack.prototype = {
|
|
|
|
constructor: Stack,
|
|
|
|
/**Creates a {Group} based on a set of figure IDs
|
|
* Group is created by 1. creating a Group and 2. setting Figure's groupId property to the new id of the Group
|
|
*@param {Array} figureIds - all the ids of {Figure}s
|
|
*@param {Number} groupId - the id of the {Group} (optional)
|
|
*@return {Number} - the id of newly created Group
|
|
**/
|
|
groupCreate: function (figureIds, groupId) {
|
|
|
|
//we should allow to create more than one temporary group
|
|
for (var i = 0; i < this.groups.length; i++) {
|
|
if (this.groups[i].permanent == false) {
|
|
throw 'Stack::groupCreate() You can not create new groups while you have temporary groups alive';
|
|
}
|
|
}
|
|
|
|
//create group
|
|
var g = new Group(groupId);
|
|
|
|
//add figures to group
|
|
for (var i = 0; i < figureIds.length; i++) {
|
|
var f = this.figureGetById(figureIds[i]);
|
|
f.groupId = g.id;
|
|
}
|
|
var bounds = g.getBounds();
|
|
g.rotationCoords.push(new Point(bounds[0] + (bounds[2] - bounds[0]) / 2, bounds[1] + (bounds[3] - bounds[1]) / 2));
|
|
g.rotationCoords.push(new Point(bounds[0] + (bounds[2] - bounds[0]) / 2, bounds[1]));
|
|
|
|
//save group to STACK
|
|
this.groups.push(g);
|
|
|
|
return g.id;
|
|
},
|
|
|
|
|
|
/**Finds a {Group} by it's id
|
|
*@param {Number} groupId - the {Group}'s id
|
|
*@return {Group} founded group of null if none finded
|
|
**/
|
|
groupGetById: function (groupId) {
|
|
for (var i = 0; i < this.groups.length; i++) {
|
|
if (this.groups[i].id == groupId) {
|
|
return this.groups[i];
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**Destroy a group by it's Id.
|
|
*I will be removed from Stack's groups and any member figure will be removed
|
|
*from group (by set the groupId to -1)
|
|
* @param {Number} groupId - the id of the group
|
|
**/
|
|
groupDestroy: function (groupId) {
|
|
var index = -1;
|
|
|
|
//search for the group
|
|
for (var i = 0; i < this.groups.length; i++) {
|
|
if (this.groups[i].id == groupId) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//remove it
|
|
if (index > -1) {
|
|
//remove Group
|
|
this.groups.splice(index, 1);
|
|
|
|
//remove the Figures from Group
|
|
var groupFigures = this.figureGetByGroupId(groupId);
|
|
for (var i = 0; i < groupFigures.length; i++) {
|
|
groupFigures[i].groupId = -1;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**Removes any temporary group*/
|
|
groupRemoveTemporary: function () {
|
|
throw Exception("Not implemented");
|
|
},
|
|
|
|
/**See if this STACK is equal to another. It is a shallow compare.
|
|
*@param {Stack} anotherStack - the other STACK object
|
|
*@return {Boolean} - true if equals, false otherwise
|
|
**/
|
|
equals: function (anotherStack) {
|
|
var msg = '';
|
|
if (!anotherStack instanceof Stack) {
|
|
return false;
|
|
msg += 'not same class';
|
|
}
|
|
|
|
|
|
|
|
//test figures
|
|
if (this.figures.length != anotherStack.figures.length) {
|
|
msg += 'not same nr of figures';
|
|
return false;
|
|
}
|
|
|
|
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (!this.figures[i].equals(anotherStack.figures[i])) {
|
|
msg += 'figures not the same';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//test groups
|
|
if (this.groups.length != anotherStack.groups.length) {
|
|
msg += 'not same nr of groups';
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < this.groups.length; i++) {
|
|
if (!this.groups[i].equals(anotherStack.groups[i])) {
|
|
msg += 'groups not the same';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//test idToIndex
|
|
for (var i = 0; i < this.figures.idToIndex; i++) {
|
|
if (this.idToIndex[i] != undefined //if not undefined
|
|
&& anotherStack.idToIndex != undefined //if not undefined
|
|
&& this.idToIndex[i] != anotherStack.idToIndex[i]) {
|
|
|
|
msg += 'not same idToIndex';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (this.currentId != anotherStack.currentId) {
|
|
msg += 'not same currentId';
|
|
return false;
|
|
}
|
|
|
|
if (msg != '') {
|
|
alert(msg);
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
/**Generates an returns a new unique ID
|
|
*@return {Number} - next id*/
|
|
generateId: function () {
|
|
return this.currentId++;
|
|
},
|
|
|
|
/**Adds a figure to the Stack of figures
|
|
*@param {Figure} figure - the figure to add
|
|
**/
|
|
figureAdd: function (figure) {
|
|
this.figures.push(figure);
|
|
this.idToIndex[figure.id] = this.figures.length - 1;
|
|
},
|
|
|
|
|
|
containerAdd: function (container) {
|
|
this.containers.push(container);
|
|
},
|
|
|
|
/*code taken from ConnectionPoint.removeConnector
|
|
*@param {Figure} figure - the figure to remove
|
|
*@deprecated
|
|
**/
|
|
figureRemove_deprecated: function (figure) {
|
|
var index = -1;
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i] == figure) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
CONNECTOR_MANAGER.connectionPointRemoveAllByParent(figure.id);
|
|
if (index > -1) {
|
|
this.figures.splice(index, 1);
|
|
for (var i = index; i < this.figures.length; i++) {
|
|
this.idToIndex[this.figures[i].id] = i;
|
|
}
|
|
}
|
|
if (index < this.figureSelectedIndex && index != -1) {
|
|
this.figureSelectedIndex--;
|
|
}
|
|
},
|
|
|
|
/**Removes a container by it's id
|
|
*@param {Number} containerId - the {Container}'s id
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
containerRemoveById: function (containerId) {
|
|
var index = -1;
|
|
for (var i = 0; i < this.containers.length; i++) {
|
|
if (this.containers[i].id === containerId) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index > -1) {
|
|
//remove figure
|
|
this.containers.splice(index, 1);
|
|
|
|
//reindex
|
|
// this.reindex();
|
|
}
|
|
},
|
|
|
|
/**Removes a figure by it's id
|
|
*@param {Number} figId - the {Figure}'s id
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
figureRemoveById: function (figId) {
|
|
var index = -1;
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i].id == figId) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index > -1) {
|
|
//remove all affected Glues
|
|
var cCPs = CONNECTOR_MANAGER.connectionPointGetAllByParent(figId); //get all connection points
|
|
var length = cCPs.length;
|
|
var k;
|
|
for (k = 0; k < length; k++) {
|
|
CONNECTOR_MANAGER.glueRemoveAllByFirstId(cCPs[k].id);
|
|
}
|
|
|
|
// remove figure's connection points
|
|
CONNECTOR_MANAGER.connectionPointRemoveAllByParent(figId);
|
|
|
|
//remove figure
|
|
this.figures.splice(index, 1);
|
|
|
|
//reindex
|
|
this.reindex();
|
|
}
|
|
},
|
|
|
|
|
|
/**Recreates the index (id, index)
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
reindex: function () {
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
this.idToIndex[this.figures[i].id] = i;
|
|
}
|
|
},
|
|
|
|
/**Deletes all the figure and reset any index*/
|
|
reset: function () {
|
|
this.figures = [];
|
|
this.figureSelectedIndex = -1;
|
|
this.currentId = 0;
|
|
},
|
|
|
|
/**Find the storage index of a figure
|
|
*@param {Figure} figure - the figure you search for
|
|
*@return {Number} - the index where you can find the Figure or -1 if not founded
|
|
**/
|
|
getIndex: function (figure) {
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i] == figure) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
|
|
|
|
/**Returns all figures from a group
|
|
*@param {Number} groupId - the id of the group
|
|
*@return {Array} - the {Array} of {Figure}s that belong to the group
|
|
**/
|
|
figureGetByGroupId: function (groupId) {
|
|
var groupFigures = [];
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i].groupId == groupId) {
|
|
groupFigures.push(this.figures[i]);
|
|
}
|
|
}
|
|
|
|
return groupFigures;
|
|
},
|
|
|
|
|
|
/**Returns all figures ids from a group
|
|
*@param {Number} groupId - the id of the group
|
|
*@return {Array} - the {Array} of {Number}s that belong to the group
|
|
**/
|
|
figureGetIdsByGroupId: function (groupId) {
|
|
var groupFiguresIds = [];
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i].groupId == groupId) {
|
|
groupFiguresIds.push(this.figures[i].id);
|
|
}
|
|
}
|
|
|
|
return groupFiguresIds;
|
|
},
|
|
|
|
/**Returns a figure by id
|
|
*@param {Number} id - the id of the figure
|
|
*@return {Figure} - the figure object or null if no figure with that id found
|
|
*TODO: use idToIndex to speed up the search....well there is no search at all :)
|
|
**/
|
|
figureGetById: function (id) {
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i].id == id) {
|
|
return this.figures[i];
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
|
|
/**
|
|
*Returns first figure glued to a connector
|
|
*@param {Number} connectorId - the id of the connector
|
|
*@return {Figure} - the figure connected, or null if none
|
|
**/
|
|
figureGetAsFirstFigureForConnector: function (connectorId) {
|
|
Log.group("Stack:figureGetAsFirstFigureForConnector");
|
|
|
|
/*Algorithm
|
|
*Connector -> first Connector's ConnectionPoint-> Glue -> Figure's ConnectionPoint -> Figure
|
|
**/
|
|
var figure = null;
|
|
|
|
//var connector = CONNECTOR_MANAGER.connectorGetById(connectorId);
|
|
Log.debug("Connector id = " + connectorId);
|
|
|
|
var startConnectionPoint = CONNECTOR_MANAGER.connectionPointGetFirstForConnector(connectorId);
|
|
Log.debug("ConnectionPoint id = " + startConnectionPoint.id);
|
|
|
|
var startGlue = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(startConnectionPoint.id)[0];
|
|
if (startGlue) {
|
|
Log.debug("Glue id1 = (" + startGlue.id1 + ", " + startGlue.id2 + ')');
|
|
|
|
var figureConnectionPoint = CONNECTOR_MANAGER.connectionPointGetById(startGlue.id1);
|
|
Log.debug("Figure's ConnectionPoint id = " + figureConnectionPoint.id);
|
|
|
|
figure = this.figureGetById(figureConnectionPoint.parentId);
|
|
}
|
|
else {
|
|
Log.debug("no glue");
|
|
}
|
|
|
|
Log.groupEnd();
|
|
|
|
return figure;
|
|
},
|
|
|
|
|
|
/**
|
|
*Returns second figure glued to a connector
|
|
*@param {Number} connectorId - the id of the connector
|
|
*@return {Figure} - the figure connected, or null if none
|
|
**/
|
|
figureGetAsSecondFigureForConnector: function (connectorId) {
|
|
Log.group("Stack:figureGetAsSecondFigureForConnector");
|
|
|
|
/*Algorithm
|
|
*Connector -> first Connector's ConnectionPoint-> Glue -> Figure's ConnectionPoint -> Figure
|
|
**/
|
|
var figure = null;
|
|
|
|
//var connector = CONNECTOR_MANAGER.connectorGetById(connectorId);
|
|
Log.debug("Connector id = " + connectorId);
|
|
|
|
var endConnectionPoint = CONNECTOR_MANAGER.connectionPointGetSecondForConnector(connectorId);
|
|
Log.debug("ConnectionPoint id = " + endConnectionPoint.id);
|
|
|
|
var startGlue = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(endConnectionPoint.id)[0];
|
|
if (startGlue) {
|
|
Log.debug("Glue id1 = (" + startGlue.id1 + ", " + startGlue.id2 + ')');
|
|
|
|
var figureConnectionPoint = CONNECTOR_MANAGER.connectionPointGetById(startGlue.id1);
|
|
Log.debug("Figure's ConnectionPoint id = " + figureConnectionPoint.id);
|
|
|
|
figure = this.figureGetById(figureConnectionPoint.parentId);
|
|
}
|
|
else {
|
|
Log.debug("no glue");
|
|
}
|
|
|
|
Log.groupEnd();
|
|
|
|
return figure;
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
*Returns the Figure's id if there is a figure for the given coordinates
|
|
*It will return the first figure we found from top to bottom (Z-order)
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Ox axis
|
|
*@return {Number} - the id of the figure or -1 if none found
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
figureGetByXY: function (x, y) {
|
|
var id = -1;
|
|
for (var i = this.figures.length - 1; i >= 0; i--) {
|
|
if (this.figures[i].contains(x, y)) {
|
|
id = this.figures[i].id;
|
|
break;
|
|
/*
|
|
*Some old stuff left from a previous version.
|
|
*TODO: delete it if no longer needed after the grouping has been (re)done
|
|
*we always want to get the group of a figure, not the figure itself where possible
|
|
*(this section only does anything when we change the order of some items
|
|
*var figure = this.figures[i];
|
|
*While the figure belongs to a group or the group is part of another group
|
|
*we will go up in the ancestor hierarchy
|
|
* while(figure.groupId >= 0){
|
|
* figure = this.figureGetById(figure.groupId);
|
|
* }
|
|
return figure;
|
|
*/
|
|
} //end if
|
|
} //end for
|
|
return id;
|
|
},
|
|
|
|
/**
|
|
*Returns the Text primitive of parent figure for the given coordinates
|
|
*It will return the first Text primitive we found from top to bottom (Z-order)
|
|
*@param {Number} fId - the id of figure - parent of target Text primitive
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Oy axis
|
|
*@return {Number} - the id value of Text primitive or -1 if none found
|
|
*@author Artyom Pokatilov <artyom.pokatilov@gmail.com>
|
|
**/
|
|
textGetByFigureXY: function (fId, x, y) {
|
|
var figureLength = this.figures.length;
|
|
for (var i = figureLength - 1; i >= 0; i--) {
|
|
var figure = this.figures[i];
|
|
if (figure.id === fId) {
|
|
var primitiveLength = figure.primitives.length;
|
|
for (var j = primitiveLength - 1; j >= 0; j--) { //top to bottom
|
|
var primitive = figure.primitives[j];
|
|
if ((primitive.oType === "Text") && primitive.contains(x, y)) {
|
|
return primitive.id;
|
|
}
|
|
}
|
|
} //end if
|
|
} //end for
|
|
return -1;
|
|
},
|
|
|
|
/**
|
|
*Returns the Text primitive of parent container for the given coordinates
|
|
*It will return the first Text primitive we found from top to bottom (Z-order)
|
|
*@param {Number} cId - the id of container - parent of target Text primitive
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Oy axis
|
|
*@return {Number} - the id value of Text primitive or -1 if none found
|
|
*@author Artyom Pokatilov <artyom.pokatilov@gmail.com>
|
|
**/
|
|
textGetByContainerXY: function (cId, x, y) {
|
|
var containerLength = this.containers.length;
|
|
for (var i = containerLength - 1; i >= 0; i--) {
|
|
var container = this.containers[i];
|
|
if (container.id === cId) {
|
|
var primitiveLength = container.primitives.length;
|
|
for (var j = primitiveLength - 1; j >= 0; j--) {
|
|
var primitive = container.primitives[j];
|
|
if ((primitive.oType == "Text") && primitive.contains(x, y)) {
|
|
return primitive.id;
|
|
}
|
|
}
|
|
} //end if
|
|
} //end for
|
|
return -1;
|
|
},
|
|
|
|
/**
|
|
*Returns the Container's id if there is a container for the given coordinates
|
|
*It will return the first container we found from top to bottom (Z-order)
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Ox axis
|
|
*@return {Number} - the id of the container or -1 if none found
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
containerGetByXY: function (x, y) {
|
|
var id = -1;
|
|
for (var i = this.containers.length - 1; i >= 0; i--) {
|
|
if (this.containers[i].contains(x, y)) {
|
|
id = this.containers[i].id;
|
|
break;
|
|
} //end if
|
|
} //end for
|
|
return id;
|
|
},
|
|
|
|
|
|
/**
|
|
*Returns the Container's id if there is a container for the given coordinates
|
|
*It will return the first container we found from top to bottom (Z-order)
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Ox axis
|
|
*@return {Number} - the id of the {Container} or -1 if none found
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
containerGetByXYOnEdge: function (x, y) {
|
|
var id = -1;
|
|
for (var i = this.containers.length - 1; i >= 0; i--) {
|
|
if (this.containers[i].onEdge(x, y)) {
|
|
id = this.containers[i].id;
|
|
break;
|
|
} //end if
|
|
} //end for
|
|
|
|
return id;
|
|
},
|
|
|
|
|
|
/**Returns a container by id
|
|
*@param {Number} id - the id of the container
|
|
*@return {Container} - the container object or null if no container with that id found
|
|
**/
|
|
containerGetById: function (id) {
|
|
for (var i = 0; i < this.containers.length; i++) {
|
|
if (this.containers[i].id == id) {
|
|
return this.containers[i];
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
*Returns an Array of Figure's id if there are figures for the given coordinates
|
|
*@param {Number} x - the value on Ox axis
|
|
*@param {Number} y - the value on Ox axis
|
|
*@return {Array} of {Number} - the array of {Figure} ids. Figures are arranged from top (closer to viewer)
|
|
* to bottom (far from viewer)
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
**/
|
|
figuresGetByXY: function (x, y) {
|
|
var ids = [];
|
|
|
|
for (var i = this.figures.length - 1; i >= 0; i--) {
|
|
if (this.figures[i].contains(x, y)) {
|
|
ids.push(this.figures[i].id);
|
|
}
|
|
}
|
|
|
|
return ids;
|
|
},
|
|
/**Returns a imageFrame by figureId
|
|
*@param {Number} figureId - the figureId of the Figure
|
|
*@return {ImageFrame} - the ImageFrame object or null if no ImageFrame with that found
|
|
**/
|
|
figuresImagePrimitiveGetByFigureId: function (figureId) {
|
|
var f = this.figureGetById(figureId);
|
|
var imageFrame = null;
|
|
for (var i = 0; i < f.primitives.length; i++) {
|
|
if (f.primitives[i].oType == 'ImageFrame') {
|
|
imageFrame = f.primitives[i];
|
|
}
|
|
}
|
|
return imageFrame;
|
|
},
|
|
/**Returns a Text by figureId
|
|
*@param {Number} figureId - the figureId of the Figure
|
|
*@return {Text} - the Text object or null if no Text with that found
|
|
**/
|
|
figuresTextPrimitiveGetByFigureId: function (figureId) {
|
|
var f = this.figureGetById(figureId);
|
|
var text = null;
|
|
if (f) {
|
|
for (var i = 0; i < f.primitives.length; i++) {
|
|
if (f.primitives[i].oType == 'Text') {
|
|
text = f.primitives[i];
|
|
}
|
|
}
|
|
}
|
|
return text;
|
|
},
|
|
|
|
/* Sets the z index of a figure
|
|
* @param {Figure} figure - the figure to move
|
|
* @param {Number} position - the Z index. The bigger value means closer to user (last painted);
|
|
* @deprecated
|
|
* */
|
|
setPosition_deprecated: function (figure, position) {
|
|
var figureIndex = -1;
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
if (this.figures[i] == figure) {
|
|
figureIndex = i;
|
|
}
|
|
}
|
|
if (figureIndex != -1 && position >= 0 && position < this.figures.length) {
|
|
//tempFigures=[];
|
|
//tempFigures.spli
|
|
//if(position<figureIndex){
|
|
var tempFigure = this.figures.splice(figureIndex, 1); //the figure to move
|
|
this.figures.splice(position, 0, tempFigure[0]);
|
|
//}
|
|
var added = false
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
this.idToIndex[this.figures[i].id] = i;
|
|
}
|
|
this.figureSelectedIndex = position;
|
|
//this.figures=tempFigures;
|
|
}
|
|
},
|
|
|
|
/** Sets the new z position of a currently selected figure (if present)
|
|
* It actually swap figures.
|
|
* <p/>
|
|
* Note: it's just a simple switch between current position and new position
|
|
* <p/>
|
|
* Zack: Is it just a switch? All you are doing is swapping, what if the user didn't want to swap, but shift up
|
|
* using this method, if you have 5 figures, and bring the very back one to the front, the front figure
|
|
* is moved to the very back, surely the correct solution is to move everything back 1, and move the selected
|
|
* figure to the front
|
|
* <p/>
|
|
* Alex: What you are saying is an insert at a certain position which is not what we want with this method.
|
|
* Maybe we should rename it swapToPosition(...) or swapIntoPosition(...)
|
|
* <p/>
|
|
* @param {Number} figureId - the id of the {Figure}
|
|
* @param {Number} newPosition - the new Z index of the figure. The bigger the value, close to user (last painted);
|
|
* @author Alex Gheorghiu <alex@scriptoid.com>
|
|
|
|
* */
|
|
swapToPosition: function (figureId, newPosition) {
|
|
var oldPosition = this.idToIndex[figureId];
|
|
|
|
if (oldPosition != -1 /**oldPosition valid*/
|
|
&& newPosition >= 0 && newPosition < this.figures.length /**newPosition in vector bounds*/) {
|
|
|
|
//update idToIndex index
|
|
this.idToIndex[figureId] = newPosition;
|
|
this.idToIndex[this.figures[newPosition].id] = oldPosition;
|
|
|
|
//switch figures
|
|
var temp = this.figures[oldPosition];
|
|
this.figures[oldPosition] = this.figures[newPosition];
|
|
this.figures[newPosition] = temp;
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
*Insert a figure into a position and shifts all other figures
|
|
*Used by moveToBack and moveToFront, sets the selected figure to the selected position, and rotates all other figures away
|
|
*@example
|
|
*[0] = 0
|
|
*[1] = 1
|
|
*[2] = 2
|
|
*
|
|
*change to
|
|
*
|
|
*@example
|
|
*[0] = 1
|
|
*[1] = 2
|
|
*[2] = 0
|
|
*
|
|
*@example
|
|
*figureA
|
|
*figureB
|
|
*figureC
|
|
*
|
|
*change to
|
|
*
|
|
*@example
|
|
*figureB
|
|
*figureC
|
|
*figureA
|
|
*@param {Number} figureId - the id of the figure
|
|
*@param {Number} newPosition - the new position of the figure
|
|
*@author Zack Newsham
|
|
*/
|
|
setPosition: function (figureId, newPosition) {
|
|
//are we moving forward or back?
|
|
var oldPosition = this.idToIndex[figureId];
|
|
var temp = this.figures[oldPosition];
|
|
var direction = -1; //move to back
|
|
if (oldPosition < newPosition) {//move to front
|
|
direction = 1;
|
|
}
|
|
Log.info(direction);
|
|
//while i between oldposition and new position, move 1 in given direction
|
|
for (var i = oldPosition; i != newPosition; i += direction) {
|
|
this.figures[i] = this.figures[i + direction]; //set the figure
|
|
this.idToIndex[this.figures[i].id] = i; //change the index
|
|
}
|
|
this.figures[newPosition] = temp; //replace the temp
|
|
this.idToIndex[this.figures[newPosition].id] = newPosition;
|
|
},
|
|
|
|
|
|
/**Test if an (x,y) is over a figure
|
|
*@param {Number} x - the x coordinates
|
|
*@param {Number} y - the y coordinates
|
|
*@return {Boolean} - true if over a figure, false otherwise
|
|
**/
|
|
figureIsOver: function (x, y) {
|
|
var found = false;
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
var figure = this.figures[i];
|
|
if (figure.contains(x, y)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
},
|
|
|
|
|
|
/**Test if an (x,y) is over a container
|
|
*@param {Number} x - the x coordinates
|
|
*@param {Number} y - the y coordinates
|
|
*@return {Boolean} - true if over a container, false otherwise
|
|
**/
|
|
containerIsOver: function (x, y) {
|
|
var found = false;
|
|
for (var i = 0; i < this.containers.length; i++) {
|
|
var container = this.containers[i];
|
|
if (container.contains(x, y)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
},
|
|
|
|
|
|
/**Test if an (x,y) is over a Container's edge (rigt on its edge)
|
|
*@param {Number} x - the x coordinates
|
|
*@param {Number} y - the y coordinates
|
|
*@return {Boolean} - true if over a container, false otherwise
|
|
**/
|
|
containerIsOnEdge: function (x, y) {
|
|
var found = false;
|
|
for (var i = 0; i < this.containers.length; i++) {
|
|
var container = this.containers[i];
|
|
if (container.onEdge(x, y)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
},
|
|
|
|
/**Return the bounds for all objects on work area (canvas).
|
|
*
|
|
*@return {Array<Number>} - returns [minX, minY, maxX, maxY] - bounds, where
|
|
* all objects on canvas (Figures/Containers/Connectors) are in the bounds.
|
|
*
|
|
*@author Artyom Pokatilov <artyom.pokatilov@gmail.com>
|
|
**/
|
|
getWorkAreaBounds: function () {
|
|
var minX;
|
|
var maxX;
|
|
var minY;
|
|
var maxY;
|
|
var unset = true; // defines if there were no object - no bounds set
|
|
|
|
// function to run for any bounds in format [minX, minY, maxX, maxY]
|
|
// compares given bounds with current values of canvas
|
|
var compareAndSet = function (bounds) {
|
|
// if minX is unset or bigger than given
|
|
if (typeof (minX) === 'undefined' || minX > bounds[0]) {
|
|
minX = bounds[0];
|
|
}
|
|
// if minY is unset or bigger than given
|
|
if (typeof (minY) === 'undefined' || minY > bounds[1]) {
|
|
minY = bounds[1];
|
|
}
|
|
// if maxX is unset or bigger than given
|
|
if (typeof (maxX) === 'undefined' || bounds[2] > maxX) {
|
|
maxX = bounds[2];
|
|
}
|
|
// if maxY is unset or bigger than given
|
|
if (typeof (maxY) === 'undefined' || bounds[3] > maxY) {
|
|
maxY = bounds[3];
|
|
}
|
|
|
|
// if once function were ran - one object setted it's bounds
|
|
unset = false;
|
|
};
|
|
|
|
var i;
|
|
// get bounds of containers
|
|
var containerLength = this.containers.length;
|
|
for (i = 0; i < containerLength; i++) {
|
|
compareAndSet(this.containers[i].getBounds());
|
|
}
|
|
|
|
// get bounds of figures
|
|
var figureLength = this.figures.length;
|
|
for (i = 0; i < figureLength; i++) {
|
|
compareAndSet(this.figures[i].getBounds());
|
|
}
|
|
|
|
// get bounds of connectors
|
|
var connectorLength = CONNECTOR_MANAGER.connectors.length;
|
|
for (i = 0; i < connectorLength; i++) {
|
|
compareAndSet(CONNECTOR_MANAGER.connectors[i].getBounds());
|
|
}
|
|
|
|
// bounds were setted/changed?
|
|
if (unset) {
|
|
// return full canvas size
|
|
return [0, 0, canvasProps.getWidth(), canvasProps.getHeight()];
|
|
} else {
|
|
// return setted new bounds
|
|
return [minX, minY, maxX, maxY];
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
*Apply a transformation to all Figures and Containers in Stack
|
|
*@param {Matrix} matrix - a matrix of numbers
|
|
*
|
|
*@author Artyom Pokatilov <artyom.pokatilov@gmail.com>
|
|
**/
|
|
transform: function (matrix) {
|
|
// translate Containers
|
|
var i;
|
|
var containerLength = this.containers.length;
|
|
for (i = 0; i < containerLength; i++) {
|
|
this.containers[i].transform(matrix);
|
|
}
|
|
|
|
// translate Figures
|
|
var figureLength = this.figures.length;
|
|
for (i = 0; i < figureLength; i++) {
|
|
// Does Figure is placed outside of container?
|
|
if (CONTAINER_MANAGER.getContainerForFigure(this.figures[i].id) === -1) {
|
|
this.figures[i].transform(matrix);
|
|
}
|
|
// otherwise it is already transformed
|
|
}
|
|
},
|
|
|
|
|
|
/**Paints all {Figure}s from back to top (Z order)
|
|
*@param {Context} context - the 2D context
|
|
*@param {boolean} ignoreSelection - if ignoreSelection is set to true selections will not be painted
|
|
**/
|
|
paint: function (context, ignoreSelection) {
|
|
// Log.group("STACK: paint");
|
|
/*The idea is to paint from bottom to top
|
|
* 1. figures (first)
|
|
* 2. connectors
|
|
* 3. handlers
|
|
* 4. selection area (last)
|
|
**/
|
|
|
|
if (DIAGRAMO.debug) {
|
|
var pos = 1;
|
|
context.save();
|
|
context.font = "10px Arial";
|
|
context.fillStyle = '#000000';
|
|
context.strokeStyle = '#000000';
|
|
context.lineWidth = 1;
|
|
context.fillText("state: " + state, 0, 10 * pos++);
|
|
context.fillText("selectedFigureId: : " + selectedFigureId, 0, 10 * pos++);
|
|
context.fillText("selectedGroupId: : " + selectedGroupId, 0, 10 * pos++);
|
|
context.fillText("selectedContainerId: : " + selectedContainerId, 0, 10 * pos++);
|
|
if (selectedGroupId != -1) {
|
|
var logGroup = this.groupGetById(selectedGroupId);
|
|
context.fillText("permanent: : " + logGroup.permanent, 0, 10 * pos++);
|
|
}
|
|
context.fillText("selectedConnectorId: : " + selectedConnectorId, 0, 10 * pos++);
|
|
if (selectedConnectorId != -1) {
|
|
var connector = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
|
|
context.fillText("connector type: : " + connector.type, 0, 10 * pos++);
|
|
}
|
|
|
|
context.restore();
|
|
}
|
|
|
|
//paint containers
|
|
for (var i = 0; i < this.containers.length; i++) {
|
|
context.save();
|
|
|
|
this.containers[i].paint(context);
|
|
|
|
context.restore();
|
|
}
|
|
//end paint containers
|
|
|
|
|
|
//paint figures
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
|
|
|
|
if (!context.save) {
|
|
alert("save() no present")
|
|
}
|
|
context.save();
|
|
|
|
this.figures[i].paint(context);
|
|
context.restore();
|
|
|
|
//if we are connecting something we should paint connection points too
|
|
if (state == STATE_CONNECTOR_PICK_FIRST || state == STATE_CONNECTOR_PICK_SECOND
|
|
|| state == STATE_CONNECTOR_MOVE_POINT) {
|
|
CONNECTOR_MANAGER.connectionPointPaint(context, this.figures[i].id);
|
|
}
|
|
|
|
} //end for
|
|
|
|
|
|
//if we are connecting something we should paint currentCloud too
|
|
if (state == STATE_CONNECTOR_PICK_FIRST || state == STATE_CONNECTOR_PICK_SECOND
|
|
|| state == STATE_CONNECTOR_MOVE_POINT) {
|
|
CONNECTOR_MANAGER.connectionCloudPaint(context);
|
|
}
|
|
|
|
|
|
//paint connector(s)
|
|
CONNECTOR_MANAGER.connectorPaint(context, selectedConnectorId);
|
|
|
|
|
|
//paint handlers for selected shape
|
|
if (state == STATE_FIGURE_SELECTED) { //FIGURE
|
|
var f = this.figureGetById(selectedFigureId);
|
|
//self dgq
|
|
if (f) {
|
|
f.style.gradientBounds = f.getBounds();
|
|
HandleManager.shapeSet(f);
|
|
}
|
|
//alert('Paint handles');
|
|
if (!ignoreSelection) {
|
|
HandleManager.paint(context);
|
|
}
|
|
}
|
|
else if (state == STATE_CONNECTOR_SELECTED) { //CONNECTOR
|
|
var c = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
|
|
HandleManager.shapeSet(c);
|
|
if (!ignoreSelection) {
|
|
HandleManager.paint(context);
|
|
}
|
|
}
|
|
else if (state == STATE_CONTAINER_SELECTED) { //CONTAINER
|
|
var cont = STACK.containerGetById(selectedContainerId);
|
|
HandleManager.shapeSet(cont);
|
|
if (!ignoreSelection) {
|
|
HandleManager.paint(context);
|
|
}
|
|
}
|
|
else if (state == STATE_GROUP_SELECTED) { //GROUP
|
|
var g = this.groupGetById(selectedGroupId);
|
|
HandleManager.shapeSet(g);
|
|
if (!ignoreSelection) {
|
|
HandleManager.paint(context);
|
|
}
|
|
}
|
|
else if (state == STATE_SELECTING_MULTIPLE) { //SELECTION
|
|
/* If shift is pressed, then leave the selected figure
|
|
* or group drawn on screen and allow drawing region in same time*/
|
|
if (SHIFT_PRESSED) {
|
|
if (selectedFigureId != -1) {
|
|
var f = this.figureGetById(selectedFigureId);
|
|
HandleManager.paint(context);
|
|
}
|
|
if (selectedGroupId != -1) {
|
|
var g = this.groupGetById(selectedGroupId);
|
|
HandleManager.paint(context);
|
|
}
|
|
}
|
|
|
|
selectionArea.paint(context);
|
|
Log.info(selectionArea.toString());
|
|
}
|
|
|
|
if (DIAGRAMO.debug) {
|
|
// Log.group("Visual debug");
|
|
var colors = {
|
|
's0': '#ff0000',
|
|
's1_1': '#009900',
|
|
's1_2': '#0033ff',
|
|
's2_1': '#00ff33',
|
|
's2_2': '#ff3399',
|
|
's2_3': '#808019',
|
|
's2_4': '#e6e64c',
|
|
's2_5': '#e67814',
|
|
's2_6': '#309bda'
|
|
};
|
|
|
|
context.save();
|
|
for (var i = 0; i < DIAGRAMO.debugSolutions.length; i++) {
|
|
var shift = 3 + i * 3;
|
|
var solution = DIAGRAMO.debugSolutions[i];
|
|
// Log.info("Solution: " + solution + " type of " + typeof(solution[2]) + " length: " + solution.length );
|
|
// Log.info("Solution points: " + solution[2]);
|
|
var points = solution[2]; //get points
|
|
|
|
|
|
//paint line
|
|
context.save();
|
|
context.strokeStyle = colors[solution[1]];
|
|
context.lineWidth = 1;
|
|
context.beginPath();
|
|
context.moveTo(points[0].x + shift, points[0].y + shift);
|
|
for (var j = 1; j < points.length; j++) {
|
|
context.lineTo(points[j].x + shift, points[j].y + shift)
|
|
}
|
|
//context.closePath();
|
|
context.stroke();
|
|
context.restore();
|
|
|
|
|
|
//paint points
|
|
for (var j = 0; j < points.length; j++) {
|
|
context.save();
|
|
// points[j].style.strokeStyle = '#FF0000';
|
|
// points[j].paint(context);
|
|
context.beginPath();
|
|
//context.strokeStyle= colors[solution[1]];
|
|
context.strokeStyle = '#cccccc';
|
|
//context.arc(points[j].x,points[j].y, 2 + i * 3, 0, Math.PI*2, true);
|
|
context.arc(points[j].x, points[j].y, 3, 0, Math.PI * 2, true);
|
|
//context.closePath();
|
|
context.stroke();
|
|
context.restore();
|
|
}
|
|
}
|
|
|
|
//paint the legend
|
|
pos += 4;
|
|
for (var solName in colors) {
|
|
context.save();
|
|
context.strokeText(solName, 0, 10 * pos);
|
|
context.strokeStyle = colors[solName];
|
|
context.beginPath();
|
|
context.moveTo(50, 10 * pos);
|
|
context.lineTo(150, 10 * pos);
|
|
//context.endPath();
|
|
context.stroke();
|
|
context.restore();
|
|
pos += 2;
|
|
}
|
|
context.restore();
|
|
|
|
// Log.groupEnd();
|
|
}
|
|
|
|
// Log.groupEnd();
|
|
},
|
|
|
|
/**Convert all STACK to SVG representation
|
|
*@return {String} - the SVG string representation*/
|
|
toSVG: function () {
|
|
var svg = '';
|
|
|
|
for (var i = 0; i < this.figures.length; i++) {
|
|
svg += this.figures[i].toSVG();
|
|
}
|
|
|
|
return svg;
|
|
}
|
|
} |