|
|
@ -322,168 +322,176 @@ Edge.prototype._getLineWidth = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
Edge.prototype._getViaCoordinates = function () { |
|
|
|
var xVia = null; |
|
|
|
var yVia = null; |
|
|
|
var factor = this.options.smoothCurves.roundness; |
|
|
|
var type = this.options.smoothCurves.type; |
|
|
|
|
|
|
|
var dx = Math.abs(this.from.x - this.to.x); |
|
|
|
var dy = Math.abs(this.from.y - this.to.y); |
|
|
|
if (type == 'discrete' || type == 'diagonalCross') { |
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { |
|
|
|
return this.via; |
|
|
|
} |
|
|
|
else if (this.options.smoothCurves.enabled == false) { |
|
|
|
return {x:0,y:0}; |
|
|
|
} |
|
|
|
else { |
|
|
|
var xVia = null; |
|
|
|
var yVia = null; |
|
|
|
var factor = this.options.smoothCurves.roundness; |
|
|
|
var type = this.options.smoothCurves.type; |
|
|
|
|
|
|
|
var dx = Math.abs(this.from.x - this.to.x); |
|
|
|
var dy = Math.abs(this.from.y - this.to.y); |
|
|
|
if (type == 'discrete' || type == 'diagonalCross') { |
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
if (type == "discrete") { |
|
|
|
xVia = dx < factor * dy ? this.from.x : xVia; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
} |
|
|
|
} |
|
|
|
if (type == "discrete") { |
|
|
|
yVia = dy < factor * dx ? this.from.y : yVia; |
|
|
|
} |
|
|
|
} |
|
|
|
if (type == "discrete") { |
|
|
|
xVia = dx < factor * dy ? this.from.x : xVia; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
else if (type == "straightCross") { |
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down
|
|
|
|
xVia = this.from.x; |
|
|
|
if (this.from.y < this.to.y) { |
|
|
|
yVia = this.to.y - (1 - factor) * dy; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
else { |
|
|
|
yVia = this.to.y + (1 - factor) * dy; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right
|
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
xVia = this.to.x - (1 - factor) * dx; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
else { |
|
|
|
xVia = this.to.x + (1 - factor) * dx; |
|
|
|
} |
|
|
|
} |
|
|
|
if (type == "discrete") { |
|
|
|
yVia = dy < factor * dx ? this.from.y : yVia; |
|
|
|
yVia = this.from.y; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (type == "straightCross") { |
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down
|
|
|
|
xVia = this.from.x; |
|
|
|
if (this.from.y < this.to.y) { |
|
|
|
yVia = this.to.y - (1-factor) * dy; |
|
|
|
else if (type == 'horizontal') { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.to.x - (1 - factor) * dx; |
|
|
|
} |
|
|
|
else { |
|
|
|
yVia = this.to.y + (1-factor) * dy; |
|
|
|
xVia = this.to.x + (1 - factor) * dx; |
|
|
|
} |
|
|
|
yVia = this.from.y; |
|
|
|
} |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right
|
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.to.x - (1-factor) * dx; |
|
|
|
else if (type == 'vertical') { |
|
|
|
xVia = this.from.x; |
|
|
|
if (this.from.y < this.to.y) { |
|
|
|
yVia = this.to.y - (1 - factor) * dy; |
|
|
|
} |
|
|
|
else { |
|
|
|
xVia = this.to.x + (1-factor) * dx; |
|
|
|
yVia = this.to.y + (1 - factor) * dy; |
|
|
|
} |
|
|
|
yVia = this.from.y; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (type == 'horizontal') { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
xVia = this.to.x - (1-factor) * dx; |
|
|
|
} |
|
|
|
else { |
|
|
|
xVia = this.to.x + (1-factor) * dx; |
|
|
|
} |
|
|
|
yVia = this.from.y; |
|
|
|
} |
|
|
|
else if (type == 'vertical') { |
|
|
|
xVia = this.from.x; |
|
|
|
if (this.from.y < this.to.y) { |
|
|
|
yVia = this.to.y - (1-factor) * dy; |
|
|
|
} |
|
|
|
else { |
|
|
|
yVia = this.to.y + (1-factor) * dy; |
|
|
|
} |
|
|
|
} |
|
|
|
else { // continuous
|
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
else { // continuous
|
|
|
|
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
// console.log(1)
|
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
xVia = this.to.x < xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
xVia = this.to.x < xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
// console.log(2)
|
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
xVia = this.to.x > xVia ? this.to.x :xVia; |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y - factor * dy; |
|
|
|
xVia = this.to.x > xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
// console.log(3)
|
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
xVia = this.to.x < xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x + factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
xVia = this.to.x < xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
// console.log(4, this.from.x, this.to.x)
|
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
xVia = this.to.x > xVia ? this.to.x : xVia; |
|
|
|
xVia = this.from.x - factor * dy; |
|
|
|
yVia = this.from.y + factor * dy; |
|
|
|
xVia = this.to.x > xVia ? this.to.x : xVia; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { |
|
|
|
if (this.from.y > this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
// console.log(5)
|
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
yVia = this.to.y > yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
yVia = this.to.y > yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
// console.log(6)
|
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
yVia = this.to.y > yVia ? this.to.y : yVia; |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y - factor * dx; |
|
|
|
yVia = this.to.y > yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
else if (this.from.y < this.to.y) { |
|
|
|
if (this.from.x < this.to.x) { |
|
|
|
// console.log(7)
|
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
yVia = this.to.y < yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
xVia = this.from.x + factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
yVia = this.to.y < yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
else if (this.from.x > this.to.x) { |
|
|
|
// console.log(8)
|
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
yVia = this.to.y < yVia ? this.to.y : yVia; |
|
|
|
xVia = this.from.x - factor * dx; |
|
|
|
yVia = this.from.y + factor * dx; |
|
|
|
yVia = this.to.y < yVia ? this.to.y : yVia; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return {x:xVia, y:yVia}; |
|
|
|
return {x: xVia, y: yVia}; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -772,7 +780,70 @@ Edge.prototype._drawArrowCenter = function(ctx) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Edge.prototype._pointOnBezier = function(t) { |
|
|
|
var via = this._getViaCoordinates(); |
|
|
|
|
|
|
|
var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; |
|
|
|
var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; |
|
|
|
|
|
|
|
return {x:x,y:y}; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* This function uses binary search to look for the point where the bezier curve crosses the border of the node. |
|
|
|
* |
|
|
|
* @param from |
|
|
|
* @param ctx |
|
|
|
* @returns {*} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Edge.prototype._findBorderPosition = function(from,ctx) { |
|
|
|
var maxIterations = 10; |
|
|
|
var iteration = 0; |
|
|
|
var low = 0; |
|
|
|
var high = 1; |
|
|
|
var pos,angle,distanceToBorder, distanceToNodes, difference; |
|
|
|
var threshold = 0.2; |
|
|
|
var node = this.to; |
|
|
|
if (from == true) { |
|
|
|
|
|
|
|
node = this.from; |
|
|
|
} |
|
|
|
|
|
|
|
while (low <= high && iteration < maxIterations) { |
|
|
|
var middle = (low + high) * 0.5; |
|
|
|
|
|
|
|
pos = this._pointOnBezier(middle); |
|
|
|
angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); |
|
|
|
distanceToBorder = node.distanceToBorder(ctx,angle); |
|
|
|
distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); |
|
|
|
difference = distanceToBorder - distanceToNodes; |
|
|
|
if (Math.abs(difference) < threshold) { |
|
|
|
break; // found
|
|
|
|
} |
|
|
|
else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
|
|
|
|
if (from == false) { |
|
|
|
low = middle; |
|
|
|
} |
|
|
|
else { |
|
|
|
high = middle; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
if (from == false) { |
|
|
|
high = middle; |
|
|
|
} |
|
|
|
else { |
|
|
|
low = middle; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
iteration++; |
|
|
|
} |
|
|
|
pos.t = middle; |
|
|
|
|
|
|
|
return pos; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Redraw a edge as a line with an arrow |
|
|
@ -787,59 +858,37 @@ Edge.prototype._drawArrow = function(ctx) { |
|
|
|
ctx.fillStyle = ctx.strokeStyle; |
|
|
|
ctx.lineWidth = this._getLineWidth(); |
|
|
|
|
|
|
|
var angle, length; |
|
|
|
//draw a line
|
|
|
|
if (this.from != this.to) { |
|
|
|
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); |
|
|
|
var dx = (this.to.x - this.from.x); |
|
|
|
var dy = (this.to.y - this.from.y); |
|
|
|
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); |
|
|
|
|
|
|
|
var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); |
|
|
|
var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; |
|
|
|
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; |
|
|
|
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; |
|
|
|
|
|
|
|
var via; |
|
|
|
if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { |
|
|
|
via = this.via; |
|
|
|
} |
|
|
|
else if (this.options.smoothCurves.enabled == true) { |
|
|
|
via = this._getViaCoordinates(); |
|
|
|
} |
|
|
|
// set vars
|
|
|
|
var angle, length, arrowPos; |
|
|
|
|
|
|
|
if (this.options.smoothCurves.enabled == true && via.x != null) { |
|
|
|
angle = Math.atan2((this.to.y - via.y), (this.to.x - via.x)); |
|
|
|
dx = (this.to.x - via.x); |
|
|
|
dy = (this.to.y - via.y); |
|
|
|
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); |
|
|
|
} |
|
|
|
var toBorderDist = this.to.distanceToBorder(ctx, angle); |
|
|
|
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; |
|
|
|
|
|
|
|
var xTo,yTo; |
|
|
|
if (this.options.smoothCurves.enabled == true && via.x != null) { |
|
|
|
xTo = (1 - toBorderPoint) * via.x + toBorderPoint * this.to.x; |
|
|
|
yTo = (1 - toBorderPoint) * via.y + toBorderPoint * this.to.y; |
|
|
|
} |
|
|
|
else { |
|
|
|
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; |
|
|
|
yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; |
|
|
|
} |
|
|
|
// if not connected to itself
|
|
|
|
if (this.from != this.to) { |
|
|
|
// draw line
|
|
|
|
this._line(ctx); |
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
|
ctx.moveTo(xFrom,yFrom); |
|
|
|
if (this.options.smoothCurves.enabled == true && via.x != null) { |
|
|
|
ctx.quadraticCurveTo(via.x,via.y,xTo, yTo); |
|
|
|
// draw arrow head
|
|
|
|
if (this.options.smoothCurves.enabled == true) { |
|
|
|
var via = this._getViaCoordinates(); |
|
|
|
arrowPos = this._findBorderPosition(false, ctx); |
|
|
|
var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) |
|
|
|
angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); |
|
|
|
} |
|
|
|
else { |
|
|
|
ctx.lineTo(xTo, yTo); |
|
|
|
angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); |
|
|
|
var dx = (this.to.x - this.from.x); |
|
|
|
var dy = (this.to.y - this.from.y); |
|
|
|
var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); |
|
|
|
var toBorderDist = this.to.distanceToBorder(ctx, angle); |
|
|
|
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; |
|
|
|
|
|
|
|
arrowPos = {}; |
|
|
|
arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; |
|
|
|
arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; |
|
|
|
} |
|
|
|
ctx.stroke(); |
|
|
|
|
|
|
|
// draw arrow at the end of the line
|
|
|
|
length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; |
|
|
|
ctx.arrow(xTo, yTo, angle, length); |
|
|
|
ctx.arrow(arrowPos.x,arrowPos.y, angle, length); |
|
|
|
ctx.fill(); |
|
|
|
ctx.stroke(); |
|
|
|
|
|
|
@ -847,9 +896,7 @@ Edge.prototype._drawArrow = function(ctx) { |
|
|
|
if (this.label) { |
|
|
|
var point; |
|
|
|
if (this.options.smoothCurves.enabled == true && via != null) { |
|
|
|
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); |
|
|
|
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); |
|
|
|
point = {x:midpointX, y:midpointY}; |
|
|
|
point = this._pointOnBezier(0.5); |
|
|
|
} |
|
|
|
else { |
|
|
|
point = this._pointOnLine(0.5); |
|
|
@ -902,8 +949,6 @@ Edge.prototype._drawArrow = function(ctx) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Calculate the distance between a point (x3,y3) and a line segment from |
|
|
|
* (x1,y1) to (x2,y2). |
|
|
|