Syntree.config_maps.arrow = {};
Syntree.config_maps.arrow.accept_unmapped_config = false;
Syntree.config_maps.arrow.map = {
id: {
require: 'number',
default_value: '#undefined',
},
/**
* Node the arrow starts at.
*
* @type {Syntree.Node}
*
* @memberof Syntree.Arrow
*/
fromNode: {
require: 'node',
},
/**
* Node the arrow ends at.
*
* @type {Syntree.Node}
*
* @memberof Syntree.Arrow
*/
toNode: {
require: 'node',
},
/**
* Path string of the arrow line.
*
* @type {string}
*
* @memberof Syntree.Arrow
*/
path: {
require: 'string',
default_value: '#undefined',
},
}
/**
* @class
* @classdesc Represents a movement arrow.
* @extends Syntree.Element
* @extends Syntree.SelectableElement
*/
Syntree.Arrow = function(config_matrix) {
this.id = config_matrix.id;
Syntree.Lib.config(config_matrix,this);
// Syntree.SelectableElement.call(this); // Extends
Syntree.Lib.extend(Syntree.SelectableElement,Syntree.Arrow,this);
this.toNode.toArrow = this;
this.fromNode.fromArrow = this;
this.updateGraphics();
new Syntree.Action('create', {
created_obj: this,
});
}
/**
* Create graphical elements and compile them into a new instance of Syntree.Graphic.
*
* @memberof Syntree.Graphic
*/
Syntree.Arrow.prototype.createGraphic = function() {
var startPoint = this.fromNode.getPosition();
var endPoint = this.toNode.getPosition();
if (Syntree.Lib.checkType(this.path, 'string')) {
var path = this.path;
} else {
var path = 'M ' + startPoint.x + ' ' + startPoint.y +
', C ' + startPoint.x + ' ' + startPoint.y +
', ' + endPoint.x + ' ' + endPoint.y +
', ' + endPoint.x + ' ' + endPoint.y;
}
var shadowLine = Syntree.snap.path(path);
shadowLine.attr({
stroke: 'lightgrey',
strokeWidth: '5px',
class: 'arrow-shadow',
id: 'arrow-shadow-' + this.id,
fill: 'none',
})
var line = Syntree.snap.path(path);
line.attr({
stroke:'black',
strokeDasharray: '5, 5',
fill: 'none',
class: 'arrow',
id: 'arrow-' + this.id,
});
$(line.node).attr('marker-end', 'url(#Triangle)');
var mid = Syntree.Lib.getMidPoint({
x1: startPoint.x,
y1: startPoint.y,
x2: endPoint.x,
y2: endPoint.y,
})
// var lockButton = Syntree.snap.image('/resources/lock.svg', mid.x, mid.y, 10, 10);
var config_matrix = {
elements_to_add: {
shadowLine: {
el_obj: shadowLine,
},
line: {
el_obj: line,
include_in_svg_string: true,
},
// lockButton: {
// el_obj: lockButton,
// }
},
states_synced: {
selected: false,
},
data_object: this,
update_map: {
selected: {
state_name: 'selected',
handler: 'boolean',
elements: {
shadowLine: {
stateTrueAttrs: {
opacity: 1,
},
stateFalseAttrs: {
opacity: 0,
},
},
handle1: {
stateTrueAttrs: {
r: 5,
},
stateFalseAttrs: {
r: 0,
},
},
handle2: {
stateTrueAttrs: {
r: 5,
},
stateFalseAttrs: {
r: 0,
},
},
}
}
}
}
this.graphic = new Syntree.Graphic(config_matrix);
var scp = this.getStartCtrlPoint();
var ecp = this.getEndCtrlPoint();
var handle1 = Syntree.snap.circle(scp.x, scp.y, 5);
var handle2 = Syntree.snap.circle(ecp.x, ecp.y, 5);
handle1.attr({
fill: 'lightblue',
stroke: 'darkblue',
class: 'handle1',
id: 'handle1-' + this.id,
});
handle2.attr({
fill: 'pink',
stroke: 'red',
class: 'handle2',
id: 'handle2-' + this.id,
});
var customDrag1 = function(dx, dy, posx, posy) {
this.attr({
cx: posx,
cy: posy,
});
var id = this.attr('id')
id = id.substr(id.lastIndexOf('-')+1, id.length);
var arrow = Syntree.Workspace.page.allElements[id];
var t = Syntree.Lib.visualToActualCoordinates(
Number(this.attr('cx')),
Number(this.attr('cy'))
);
arrow.setStartCtrlPoint(
t.x,
t.y
);
arrow.updateGraphics();
}
var customDrag2 = function(dx, dy, posx, posy) {
this.attr({
cx: posx,
cy: posy,
});
var id = this.attr('id')
id = id.substr(id.lastIndexOf('-')+1, id.length);
var arrow = Syntree.Workspace.page.allElements[id];
var t = Syntree.Lib.visualToActualCoordinates(
Number(this.attr('cx')),
Number(this.attr('cy'))
);
arrow.setEndCtrlPoint(
t.x,
t.y
);
arrow.updateGraphics();
}
handle1.drag(customDrag1);
handle2.drag(customDrag2);
this.graphic.addElement('handle1', {
el_obj: handle1,
});
this.graphic.addElement('handle2', {
el_obj: handle2,
});
if (!Syntree.Lib.checkType(this.path, 'string')) {
var path = this.graphic.getEl('line').attr('path');
var fInter = Snap.path.intersection(path, this.fromNode.getPath());
fInter = fInter[fInter.length-1];
var tInter = Snap.path.intersection(path, this.toNode.getPath());
tInter = tInter[tInter.length-1];
this.setStartPoint(fInter.x, fInter.y);
this.setEndPoint(tInter.x, tInter.y);
var xsmooth1 = fInter.x > tInter.x ? -10 : 10;
var xsmooth2 = fInter.x > tInter.x ? 10 : -10;
this.setStartCtrlPoint(fInter.x+xsmooth1, fInter.y+30);
this.setEndCtrlPoint(tInter.x+xsmooth2, tInter.y+30);
}
}
/**
* Get start point of path.
*
* @returns {object} - x and y coordinates
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.getStartPoint = function() {
var path = this.graphic.getEl('line').attr('path');
path = path.split(',');
var start = path[0];
start = start.split(' ');
start = {
x: Number(start[1].trim()),
y: Number(start[2].trim()),
}
return start;
}
/**
* Set start point of path.
*
* @param {number} - x coordinate
* @param {number} - y coordinate
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.setStartPoint = function(x, y) {
var newStart = 'M ' + x + ' ' + y + ',';
var path = this.graphic.getEl('line').attr('path');
path = path.substr(path.indexOf(',')+1, path.length);
path = newStart + path;
this.graphic.getEl('line').attr('path', path);
}
/**
* Set end point of path.
*
* @param {number} - x coordinate
* @param {number} - y coordinate
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.setEndPoint = function(x,y) {
var newEnd = ', ' + x + ' ' + y;
var path = this.graphic.getEl('line').attr('path');
path = path.substr(0, path.lastIndexOf(','));
path = path + newEnd;
this.graphic.getEl('line').attr('path', path);
}
/**
* Get end point of path.
*
* @returns {object} - x and y coordinates
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.getEndPoint = function() {
var path = this.graphic.getEl('line').attr('path');
path = path.split(',');
var end = path[path.length-1];
end = end.split(' ');
end = {
x: Number(end[1].trim()),
y: Number(end[2].trim()),
}
return end;
}
Syntree.Arrow.prototype.toString = function() {
return '[object Arrow]'
}
/**
* Custom addition to Syntree.Element#updateGraphics.
*
* @see Syntree.Element#updateGraphics
*/
Syntree.Arrow.prototype.__updateGraphics = function() {
var fBBox = this.fromNode.getLabelBBox();
var tBBox = this.toNode.getLabelBBox();
this.setStartPoint(fBBox.cx, fBBox.cy);
this.setEndPoint(tBBox.cx, tBBox.cy);
var scp = this.getStartCtrlPoint();
var ecp = this.getEndCtrlPoint();
this.graphic.getEl('handle1').attr({
cx: scp.x,
cy: scp.y,
});
this.graphic.getEl('handle2').attr({
cx: ecp.x,
cy: ecp.y,
});
var path = this.graphic.getEl('line').attr('path');
var tInter = Snap.path.intersection(path, this.toNode.getPath());
if (this.fromNode.getLabelContent().match(/^\s.*$|^$/)) {
var pos = this.fromNode.getPosition();
this.setStartPoint(pos.x, pos.y);
} else {
var fInter = Snap.path.intersection(path, this.fromNode.getPath());
if (fInter.length > 0) {
fInter = fInter.reduce(function(l, e) {
return e.t1 < l.t1 ? e : l;
});
this.setStartPoint(fInter.x, fInter.y);
}
}
if (this.toNode.getLabelContent().match(/^\s.*$|^$/)) {
var pos = this.toNode.getPosition();
this.setEndPoint(pos.x, pos.y);
} else {
if (tInter.length > 0) {
tInter = tInter.reduce(function(l, e) {
return e.t1 > l.t1 ? e : l;
});
this.setEndPoint(tInter.x, tInter.y);
}
}
this.graphic.getEl('shadowLine').attr({
path: this.graphic.getEl('line').attr('path'),
})
}
/**
* Custom addition to Syntree.Element#delete.
*
* @see Syntree.Element#delete
*/
Syntree.Arrow.prototype.__delete = function() {
silent = Syntree.Lib.checkArg(silent, 'boolean', false);
console.log('deleting arrow');
this.fromNode.fromArrow = undefined;
this.toNode.toArrow = undefined;
new Syntree.Action('delete', {
deleted_obj: this,
fromNode: this.fromNode,
toNode: this.toNode,
path: this.graphic.getEl('line').attr('path'),
});
}
/**
* Get control point for start of path.
*
* @returns {object} - x and y coordinates
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.getStartCtrlPoint = function() {
var path = this.graphic.getEl('line').attr('path');
path = path.split('C')[1];
path = path.split(',')[0].trim().split(' ');
return {
x: Number(path[0]),
y: Number(path[1]),
}
}
/**
* Set control point for start of path.
*
* @param {number} - x coordinate
* @param {number} - y coordinate
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.setStartCtrlPoint = function(x, y) {
var path = this.graphic.getEl('line').attr('path');
var half1 = path.substr(0, path.indexOf('C') + 2);
var half2 = path.substr(path.lastIndexOf(',', path.lastIndexOf(',')-1));
var newPoint = x + ' ' + y;
this.graphic.getEl('line').attr('path', half1 + newPoint + half2);
}
/**
* Set control point for end of path.
*
* @param {number} - x coordinate
* @param {number} - y coordinate
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.setEndCtrlPoint = function(x,y) {
var path = this.graphic.getEl('line').attr('path');
var half1 = path.substr(0, path.indexOf(',', path.indexOf('C'))+2);
var half2 = path.substr(path.lastIndexOf(',', path.length));
var newPoint = x + ' ' + y;
this.graphic.getEl('line').attr('path', half1 + newPoint + half2);
}
/**
* Get control point for end of path.
*
* @returns {object} - x and y coordinates
*
* @see Syntree.Arrow#path
*/
Syntree.Arrow.prototype.getEndCtrlPoint = function() {
var path = this.graphic.getEl('line').attr('path');
path = path.split('C')[1];
path = path.split(',')[1].trim().split(' ');
return {
x: Number(path[0]),
y: Number(path[1]),
}
}