Browse Source

unified pointOnLine and pointOnBezier to pointOnEdge

flowchartTest
Alex de Mulder 9 years ago
parent
commit
ec6a474de5
3 changed files with 125 additions and 269 deletions
  1. +59
    -132
      dist/vis.js
  2. +7
    -7
      examples/network/01_basic_usage.html
  3. +59
    -130
      lib/network/modules/components/edges/EdgeMain.js

+ 59
- 132
dist/vis.js View File

@ -27437,7 +27437,7 @@ return /******/ (function(modules) { // webpackBootstrap
*/
value: function draw(ctx) {
var via = this.drawLine(ctx);
this.drawArrows(ctx);
this.drawArrows(ctx, via);
this.drawLabel(ctx, via);
},
writable: true,
@ -27455,13 +27455,15 @@ return /******/ (function(modules) { // webpackBootstrap
configurable: true
},
drawArrows: {
value: function drawArrows(ctx) {
value: function drawArrows(ctx, viaNode) {
if (this.options.arrows.from.enabled === true) {
this._drawArrowHead(ctx, "from");
this._drawArrowHead(ctx, "from", viaNode);
}
if (this.options.arrows.middle.enabled === true) {
this._drawArrowHead(ctx, "middle", viaNode);
}
//if (this.options.arrows.middle.enabled === true) {this._drawArrowCenter(ctx, via);}
if (this.options.arrows.to.enabled === true) {
this._drawArrowHead(ctx, "to");
this._drawArrowHead(ctx, "to", viaNode);
}
},
@ -27469,20 +27471,13 @@ return /******/ (function(modules) { // webpackBootstrap
configurable: true
},
drawLabel: {
value: function drawLabel(ctx, via) {
value: function drawLabel(ctx, viaNode) {
if (this.label !== undefined) {
// set style
var node1 = this.from;
var node2 = this.to;
if (node1.id != node2.id) {
var point;
if (this.options.smooth.enabled == true && via != undefined) {
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 };
} else {
point = this._pointOnLine(0.5);
}
var point = this._pointOnEdge(0.5, viaNode);
this._label(ctx, this.label, point.x, point.y);
} else {
var x, y;
@ -28101,27 +28096,36 @@ return /******/ (function(modules) { // webpackBootstrap
writable: true,
configurable: true
},
_pointOnLine: {
_pointOnEdge: {
/**
* Get a point on a line
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
* @return {Object} point
* Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
* @param percentage
* @param via
* @returns {{x: number, y: number}}
* @private
*/
value: function _pointOnLine(percentage) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
};
value: function _pointOnEdge(percentage) {
var via = arguments[1] === undefined ? this._getViaCoordinates() : arguments[1];
if (this.options.smooth.enabled == false) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
};
} else {
var t = percentage;
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 };
}
},
writable: true,
configurable: true
},
_pointOnCircle: {
/**
* Get a point on a circle
* @param {Number} x
@ -28141,93 +28145,6 @@ return /******/ (function(modules) { // webpackBootstrap
writable: true,
configurable: true
},
_drawArrowCenter: {
/**
* Redraw a edge as a line with an arrow halfway the line
* Draw this edge in the given canvas
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
* @param {CanvasRenderingContext2D} ctx
* @private
*/
value: function _drawArrowCenter(ctx) {
var point;
// set style
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
// draw line
var via = this._line(ctx);
var angle = Math.atan2(this.to.y - this.from.y, this.to.x - this.from.x);
var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor;
// draw an arrow halfway the line
if (this.options.smooth.enabled == true && via != undefined) {
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 };
} else {
point = this._pointOnLine(0.5);
}
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();
ctx.stroke();
// draw label
if (this.label) {
this._label(ctx, this.label, point.x, point.y);
}
} else {
// draw circle
var x, y;
var radius = 25;
var node = this.from;
if (!node.width) {
node.resize(ctx);
}
if (node.width > node.height) {
x = node.x + node.width * 0.5;
y = node.y - radius;
} else {
x = node.x + radius;
y = node.y - node.height * 0.5;
}
this._circle(ctx, x, y, radius);
// draw all arrows
var angle = 0.2 * Math.PI;
var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor;
point = this._pointOnCircle(x, y, radius, 0.5);
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();
ctx.stroke();
// draw label
if (this.label) {
point = this._pointOnCircle(x, y, radius, 0.5);
this._label(ctx, this.label, point.x, point.y);
}
}
},
writable: true,
configurable: true
},
_pointOnBezier: {
value: function _pointOnBezier(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 };
},
writable: true,
configurable: true
},
_findBorderPositionBezier: {
/**
@ -28238,6 +28155,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private
*/
value: function _findBorderPositionBezier(nearNode, ctx) {
var viaNode = arguments[2] === undefined ? this._getViaCoordinates() : arguments[2];
var maxIterations = 10;
var iteration = 0;
var low = 0;
@ -28254,7 +28172,7 @@ return /******/ (function(modules) { // webpackBootstrap
while (low <= high && iteration < maxIterations) {
var middle = (low + high) * 0.5;
pos = this._pointOnBezier(middle);
pos = this._pointOnEdge(middle, viaNode);
angle = Math.atan2(node.y - pos.y, node.x - pos.x);
distanceToBorder = node.distanceToBorder(ctx, angle);
distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
@ -28344,7 +28262,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @param guideOffset
* @private
*/
value: function _drawArrowHead(ctx, position) {
value: function _drawArrowHead(ctx, position, viaNode) {
// set style
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
@ -28369,28 +28287,36 @@ return /******/ (function(modules) { // webpackBootstrap
node2 = this.from;
guideOffset = -0.1;
scaleFactor = this.options.arrows.to.scaleFactor;
} else {
node1 = this.to;
node2 = this.from;
scaleFactor = this.options.arrows.middle.scaleFactor;
}
// if not connected to itself
if (node1 != node2) {
// draw arrow head
if (this.options.smooth.enabled == true) {
arrowPos = this._findBorderPositionBezier(node1, ctx);
var guidePos = this._pointOnBezier(Math.max(0, arrowPos.t + guideOffset));
angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x);
if (position !== "middle") {
// draw arrow head
if (this.options.smooth.enabled == true) {
arrowPos = this._findBorderPositionBezier(node1, ctx, viaNode);
var guidePos = this._pointOnEdge(Math.max(0, Math.min(1, arrowPos.t + guideOffset)), viaNode);
angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x);
} else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
var dx = node1.x - node2.x;
var dy = node1.y - node2.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) * node2.x + toBorderPoint * node1.x;
arrowPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
}
} else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
var dx = node1.x - node2.x;
var dy = node1.y - node2.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) * node2.x + toBorderPoint * node1.x;
arrowPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
arrowPos = this._pointOnEdge(0.6, viaNode); // this is 0.6 to account for the size of the arrow.
}
// draw arrow at the end of the line
length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(arrowPos.x, arrowPos.y, angle, length);
@ -28418,14 +28344,15 @@ return /******/ (function(modules) { // webpackBootstrap
if (position == "from") {
point = this._findBorderPositionCircle(x, y, radius, node1, 0.25, 0.6, -1, ctx);
angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
} else {
} else if (position == "to") {
point = this._findBorderPositionCircle(x, y, radius, node1, 0.6, 0.8, 1, ctx);
angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
} else {
point = this._pointOnCircle(x, y, radius, 0.175);
angle = 3.9269908169872414; // == 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
// draw all arrows
// draw the arrowhead
var length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();

+ 7
- 7
examples/network/01_basic_usage.html View File

@ -22,9 +22,9 @@
<script type="text/javascript">
// create an array with nodes
var nodes = [
// {id: 1, label: 'Node 1'},
// {id: 2, label: 'Node 2'},
// {id: 3, label: 'Node 3'},
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2'},
{id: 3, label: 'Node 3'},
{id: 4, label: 'Node 4'},
{id: 5, label: 'Node 5', shape:'database'}
];
@ -32,10 +32,10 @@
// create an array with edges
var edges = [
// {from: 1, to: 2, label:'bla', font:{size: 15, face: 'arial', color: 'red'}},
// {from: 1, to: 3, arrows:{from:true, to:true, middle: true}, label:'bla'},
// {from: 1, to: 1, arrows:{from:true, to:true, middle: true}, smooth:false, label:'bla'},
{from: 4, to: 4, arrows:{from:true, to:true, middle: true}, smooth:false, label:'bla'},
{from: 5, to: 5, arrows:{from:true, to:true, middle: true}, smooth:false, label:'bla'},
{from: 1, to: 3, arrows:{from:false, to:false, middle: true}, label:'bla', dashes:true},
{from: 1, to: 2, arrows:{from:false, to:false, middle: true}, smooth:false, label:'bla'},
{from: 4, to: 4, arrows:{from:false, to:false, middle: true}, smooth:false, label:'bla'},
{from: 5, to: 5, arrows:{from:false, to:false, middle: true}, smooth:false, label:'bla'},
// {from: 2, to: 4},
// {from: 2, to: 5}
];

+ 59
- 130
lib/network/modules/components/edges/EdgeMain.js View File

@ -280,8 +280,8 @@ class Edge {
*/
draw(ctx) {
let via = this.drawLine(ctx);
this.drawArrows(ctx);
this.drawLabel(ctx, via);
this.drawArrows(ctx, via);
this.drawLabel (ctx, via);
}
drawLine(ctx) {
@ -293,29 +293,21 @@ class Edge {
}
}
drawArrows(ctx) {
if (this.options.arrows.from.enabled === true) {this._drawArrowHead(ctx,'from');}
//if (this.options.arrows.middle.enabled === true) {this._drawArrowCenter(ctx, via);}
if (this.options.arrows.to.enabled === true) {this._drawArrowHead(ctx,'to');}
drawArrows(ctx, viaNode) {
if (this.options.arrows.from.enabled === true) {this._drawArrowHead(ctx,'from', viaNode);}
if (this.options.arrows.middle.enabled === true) {this._drawArrowHead(ctx,'middle', viaNode);}
if (this.options.arrows.to.enabled === true) {this._drawArrowHead(ctx,'to', viaNode);}
}
drawLabel(ctx, via) {
drawLabel(ctx, viaNode) {
if (this.label !== undefined) {
// set style
var node1 = this.from;
var node2 = this.to;
if (node1.id != node2.id) {
var point;
if (this.options.smooth.enabled == true && via != undefined) {
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};
}
else {
point = this._pointOnLine(0.5);
}
var point = this._pointOnEdge(0.5, viaNode);
this._label(ctx, this.label, point.x, point.y);
}
else {
@ -940,18 +932,27 @@ class Edge {
/**
* Get a point on a line
* @param {Number} percentage. Value between 0 (line start) and 1 (line end)
* @return {Object} point
* Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
* @param percentage
* @param via
* @returns {{x: number, y: number}}
* @private
*/
_pointOnLine(percentage) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
_pointOnEdge(percentage, via = this._getViaCoordinates()) {
if (this.options.smooth.enabled == false) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
}
}
}
else {
var t = percentage;
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};
}
}
/**
* Get a point on a circle
@ -970,90 +971,6 @@ class Edge {
}
}
/**
* Redraw a edge as a line with an arrow halfway the line
* Draw this edge in the given canvas
* The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
* @param {CanvasRenderingContext2D} ctx
* @private
*/
_drawArrowCenter(ctx) {
var point;
// set style
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
// draw line
var via = this._line(ctx);
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor;
// draw an arrow halfway the line
if (this.options.smooth.enabled == true && via != undefined) {
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};
}
else {
point = this._pointOnLine(0.5);
}
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();
ctx.stroke();
// draw label
if (this.label) {
this._label(ctx, this.label, point.x, point.y);
}
}
else {
// draw circle
var x, y;
var radius = 25;
var node = this.from;
if (!node.width) {
node.resize(ctx);
}
if (node.width > node.height) {
x = node.x + node.width * 0.5;
y = node.y - radius;
}
else {
x = node.x + radius;
y = node.y - node.height * 0.5;
}
this._circle(ctx, x, y, radius);
// draw all arrows
var angle = 0.2 * Math.PI;
var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor;
point = this._pointOnCircle(x, y, radius, 0.5);
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();
ctx.stroke();
// draw label
if (this.label) {
point = this._pointOnCircle(x, y, radius, 0.5);
this._label(ctx, this.label, point.x, point.y);
}
}
}
_pointOnBezier(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.
*
@ -1061,7 +978,7 @@ class Edge {
* @returns {*}
* @private
*/
_findBorderPositionBezier(nearNode, ctx) {
_findBorderPositionBezier(nearNode, ctx, viaNode = this._getViaCoordinates()) {
var maxIterations = 10;
var iteration = 0;
var low = 0;
@ -1078,7 +995,7 @@ class Edge {
while (low <= high && iteration < maxIterations) {
var middle = (low + high) * 0.5;
pos = this._pointOnBezier(middle);
pos = this._pointOnEdge(middle, viaNode);
angle = Math.atan2((node.y - pos.y), (node.x - pos.x));
distanceToBorder = node.distanceToBorder(ctx, angle);
distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
@ -1166,7 +1083,7 @@ class Edge {
* @param guideOffset
* @private
*/
_drawArrowHead(ctx,position) {
_drawArrowHead(ctx,position,viaNode) {
// set style
ctx.strokeStyle = this._getColor(ctx);
ctx.fillStyle = ctx.strokeStyle;
@ -1193,28 +1110,38 @@ class Edge {
guideOffset = -0.1;
scaleFactor = this.options.arrows.to.scaleFactor;
}
else {
node1 = this.to;
node2 = this.from;
scaleFactor = this.options.arrows.middle.scaleFactor;
}
// if not connected to itself
if (node1 != node2) {
// draw arrow head
if (this.options.smooth.enabled == true) {
arrowPos = this._findBorderPositionBezier(node1, ctx);
var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t + guideOffset))
angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x));
if (position !== 'middle') {
// draw arrow head
if (this.options.smooth.enabled == true) {
arrowPos = this._findBorderPositionBezier(node1, ctx, viaNode);
var guidePos = this._pointOnEdge(Math.max(0.0,Math.min(1.0,arrowPos.t + guideOffset)), viaNode);
angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x));
}
else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
var dx = (node1.x - node2.x);
var dy = (node1.y - node2.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) * node2.x + toBorderPoint * node1.x;
arrowPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
}
}
else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
var dx = (node1.x - node2.x);
var dy = (node1.y - node2.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) * node2.x + toBorderPoint * node1.x;
arrowPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
arrowPos = this._pointOnEdge(0.6, viaNode); // this is 0.6 to account for the size of the arrow.
}
// draw arrow at the end of the line
length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(arrowPos.x, arrowPos.y, angle, length);
@ -1245,14 +1172,16 @@ class Edge {
point = this._findBorderPositionCircle(x, y, radius, node1, 0.25, 0.6, -1, ctx);
angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
else {
else if (position == 'to') {
point = this._findBorderPositionCircle(x, y, radius, node1, 0.6, 0.8, 1, ctx);
angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
}
else {
point = this._pointOnCircle(x,y,radius,0.175);
angle = 3.9269908169872414; // == 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
// draw all arrows
// draw the arrowhead
var length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(point.x, point.y, angle, length);
ctx.fill();

Loading…
Cancel
Save