Browse Source

- Altered edges for arrows and added the arrowStrikethrough option.

codeClimate
Alex de Mulder 9 years ago
parent
commit
6e9e4cc3d5
14 changed files with 437 additions and 323 deletions
  1. +2
    -0
      HISTORY.md
  2. +211
    -159
      dist/vis.js
  3. +7
    -0
      docs/network/edges.html
  4. +1
    -0
      lib/network/modules/EdgesHandler.js
  5. +33
    -14
      lib/network/modules/LayoutEngine.js
  6. +36
    -7
      lib/network/modules/components/Edge.js
  7. +16
    -7
      lib/network/modules/components/edges/BezierEdgeDynamic.js
  8. +21
    -15
      lib/network/modules/components/edges/BezierEdgeStatic.js
  9. +14
    -12
      lib/network/modules/components/edges/CubicBezierEdge.js
  10. +7
    -4
      lib/network/modules/components/edges/StraightEdge.js
  11. +51
    -51
      lib/network/modules/components/edges/util/EdgeBase.js
  12. +1
    -1
      lib/network/modules/components/nodes/shapes/Dot.js
  13. +4
    -0
      lib/network/options.js
  14. +33
    -53
      test/networkTest.html

+ 2
- 0
HISTORY.md View File

@ -4,7 +4,9 @@ http://visjs.org
## not yet released, version 4.12.1-SNAPSHOT
### Network
- Altered edges for arrows and added the arrowStrikethrough option.
## 2016-01-08, version 4.12.0

+ 211
- 159
dist/vis.js View File

@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
* @version 4.12.0
* @date 2016-01-08
* @version 4.12.1-SNAPSHOT
* @date 2016-01-11
*
* @license
* Copyright (C) 2011-2016 Almende B.V, http://almende.com
@ -11047,14 +11047,14 @@ return /******/ (function(modules) { // webpackBootstrap
var PropagatingHammer = function(element, options) {
var o = Object.create(_options);
if (options) Hammer.assign(o, options);
if (options) Hammer.extend(o, options);
return propagating(new Hammer(element, o), o);
};
Hammer.assign(PropagatingHammer, Hammer);
Hammer.extend(PropagatingHammer, Hammer);
PropagatingHammer.Manager = function (element, options) {
var o = Object.create(_options);
if (options) Hammer.assign(o, options);
if (options) Hammer.extend(o, options);
return propagating(new Hammer.Manager(element, o), o);
};
@ -11067,9 +11067,7 @@ return /******/ (function(modules) { // webpackBootstrap
// attach to DOM element
var element = hammer.element;
if(!element.hammer) element.hammer = [];
element.hammer.push(wrapper);
element.hammer = wrapper;
// register an event to catch the start of a gesture and store the
// target in a singleton
@ -11150,10 +11148,7 @@ return /******/ (function(modules) { // webpackBootstrap
wrapper.destroy = function () {
// Detach from DOM element
var hammers = hammer.element.hammer;
var idx = hammers.indexOf(wrapper);
if(idx !== -1) hammers.splice(idx,1);
if(!hammers.length) delete hammer.element.hammer;
delete hammer.element.hammer;
// clear all handlers
wrapper._handlers = {};
@ -11194,30 +11189,19 @@ return /******/ (function(modules) { // webpackBootstrap
stopped = true;
};
//wrap the srcEvent's stopPropagation to also stop hammer propagation:
var srcStop = event.srcEvent.stopPropagation;
if(typeof srcStop == "function") {
event.srcEvent.stopPropagation = function(){
srcStop();
event.stopPropagation();
}
}
// attach firstTarget property to the event
event.firstTarget = _firstTarget;
// propagate over all elements (until stopped)
var elem = _firstTarget;
while (elem && !stopped) {
if(elem.hammer){
var _handlers;
for(var k = 0; k < elem.hammer.length; k++){
_handlers = elem.hammer[k]._handlers[event.type];
if(_handlers) for (var i = 0; i < _handlers.length && !stopped; i++) {
_handlers[i](event);
}
var _handlers = elem.hammer && elem.hammer._handlers[event.type];
if (_handlers) {
for (var i = 0; i < _handlers.length && !stopped; i++) {
_handlers[i](event);
}
}
elem = elem.parentNode;
}
}
@ -30631,7 +30615,7 @@ return /******/ (function(modules) { // webpackBootstrap
key: 'distanceToBorder',
value: function distanceToBorder(ctx, angle) {
this.resize(ctx);
return this.options.size + this.options.borderWidth;
return this.options.size;
}
}]);
@ -31357,6 +31341,7 @@ return /******/ (function(modules) { // webpackBootstrap
middle: { enabled: false, scaleFactor: 1 },
from: { enabled: false, scaleFactor: 1 }
},
arrowStrikethrough: false,
color: {
color: '#848484',
highlight: '#848484',
@ -32063,21 +32048,45 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: 'draw',
value: function draw(ctx) {
var via = this.edgeType.drawLine(ctx, this.selected, this.hover);
this.drawArrows(ctx, via);
this.drawLabel(ctx, via);
// get the via node from the edge type
var viaNode = this.edgeType.getViaNode();
var arrowData = {};
// restore edge targets to defaults
this.edgeType.fromPoint = this.from;
this.edgeType.toPoint = this.to;
// from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) {
arrowData.from = this.edgeType.getArrowData(ctx, 'from', viaNode, this.selected, this.hover);
if (this.options.arrowStrikethrough === true) this.edgeType.fromPoint = arrowData.from.core;
}
if (this.options.arrows.to.enabled === true) {
arrowData.to = this.edgeType.getArrowData(ctx, 'to', viaNode, this.selected, this.hover);
if (this.options.arrowStrikethrough === true) this.edgeType.toPoint = arrowData.to.core;
}
// the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly.
if (this.options.arrows.middle.enabled === true) {
arrowData.middle = this.edgeType.getArrowData(ctx, 'middle', viaNode, this.selected, this.hover);
}
// draw everything
this.edgeType.drawLine(ctx, this.selected, this.hover, viaNode);
this.drawArrows(ctx, arrowData);
this.drawLabel(ctx, viaNode);
}
}, {
key: 'drawArrows',
value: function drawArrows(ctx, viaNode) {
value: function drawArrows(ctx, arrowData) {
if (this.options.arrows.from.enabled === true) {
this.edgeType.drawArrowHead(ctx, 'from', viaNode, this.selected, this.hover);
this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.from);
}
if (this.options.arrows.middle.enabled === true) {
this.edgeType.drawArrowHead(ctx, 'middle', viaNode, this.selected, this.hover);
this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.middle);
}
if (this.options.arrows.to.enabled === true) {
this.edgeType.drawArrowHead(ctx, 'to', viaNode, this.selected, this.hover);
this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.to);
}
}
}, {
@ -32210,7 +32219,7 @@ return /******/ (function(modules) { // webpackBootstrap
var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
var fields = ['id', 'from', 'hidden', 'hoverWidth', 'label', 'labelHighlightBold', 'length', 'line', 'opacity', 'physics', 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width'];
var fields = ['arrowStrikethrough', 'id', 'from', 'hidden', 'hoverWidth', 'label', 'labelHighlightBold', 'length', 'line', 'opacity', 'physics', 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width'];
// only deep extend the items in the field array. These do not have shorthand.
util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion);
@ -32354,34 +32363,25 @@ return /******/ (function(modules) { // webpackBootstrap
_createClass(CubicBezierEdge, [{
key: '_line',
value: function _line(ctx) {
value: function _line(ctx, viaNodes) {
// get the coordinates of the support points.
var _getViaCoordinates2 = this._getViaCoordinates();
var _getViaCoordinates22 = _slicedToArray(_getViaCoordinates2, 2);
var via1 = _getViaCoordinates22[0];
var via2 = _getViaCoordinates22[1];
var returnValue = [via1, via2];
var via1 = viaNodes[0];
var via2 = viaNodes[1];
// start drawing the line.
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (via1.x === undefined) {
ctx.lineTo(this.to.x, this.to.y);
returnValue = undefined;
if (viaNodes === undefined || via1.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
} else {
ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.to.x, this.to.y);
ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
return returnValue;
}
}, {
key: '_getViaCoordinates',
@ -32393,7 +32393,7 @@ return /******/ (function(modules) { // webpackBootstrap
y1 = undefined,
x2 = undefined,
y2 = undefined;
var roundness = this.options.smooth.roundness;;
var roundness = this.options.smooth.roundness;
// horizontal if x > y or if direction is forced or if direction is horizontal
if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') {
@ -32410,6 +32410,11 @@ return /******/ (function(modules) { // webpackBootstrap
return [{ x: x1, y: y1 }, { x: x2, y: y2 }];
}
}, {
key: 'getViaNode',
value: function getViaNode() {
return this._getViaCoordinates();
}
}, {
key: '_findBorderPosition',
value: function _findBorderPosition(nearNode, ctx) {
@ -32451,8 +32456,8 @@ return /******/ (function(modules) { // webpackBootstrap
vec[1] = 3 * t * Math.pow(1 - t, 2);
vec[2] = 3 * Math.pow(t, 2) * (1 - t);
vec[3] = Math.pow(t, 3);
var x = vec[0] * this.from.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.to.x;
var y = vec[0] * this.from.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.to.y;
var x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x;
var y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y;
return { x: x, y: y };
}
@ -32723,6 +32728,8 @@ return /******/ (function(modules) { // webpackBootstrap
this.color = {};
this.selectionWidth = 2;
this.hoverWidth = 1.5;
this.fromPoint = this.from;
this.toPoint = this.to;
}
_createClass(EdgeBase, [{
@ -32754,25 +32761,23 @@ return /******/ (function(modules) { // webpackBootstrap
*/
}, {
key: 'drawLine',
value: function drawLine(ctx, selected, hover) {
value: function drawLine(ctx, selected, hover, viaNode) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.lineWidth = this.getLineWidth(selected, hover);
var via = undefined;
if (this.options.dashes !== false) {
via = this._drawDashedLine(ctx);
this._drawDashedLine(ctx, viaNode);
} else {
via = this._drawLine(ctx);
this._drawLine(ctx, viaNode);
}
return via;
}
}, {
key: '_drawLine',
value: function _drawLine(ctx) {
var via = undefined;
value: function _drawLine(ctx, viaNode, fromPoint, toPoint) {
if (this.from != this.to) {
// draw line
via = this._line(ctx);
this._line(ctx, viaNode, fromPoint, toPoint);
} else {
var _getCircleData2 = this._getCircleData(ctx);
@ -32784,12 +32789,10 @@ return /******/ (function(modules) { // webpackBootstrap
this._circle(ctx, x, y, radius);
}
return via;
}
}, {
key: '_drawDashedLine',
value: function _drawDashedLine(ctx) {
var via = undefined;
value: function _drawDashedLine(ctx, viaNode, fromPoint, toPoint) {
ctx.lineCap = 'round';
var pattern = [5, 5];
if (Array.isArray(this.options.dashes) === true) {
@ -32807,7 +32810,7 @@ return /******/ (function(modules) { // webpackBootstrap
// draw the line
if (this.from != this.to) {
// draw line
via = this._line(ctx);
this._line(ctx, viaNode);
} else {
var _getCircleData3 = this._getCircleData(ctx);
@ -32848,7 +32851,6 @@ return /******/ (function(modules) { // webpackBootstrap
// disable shadows for other elements.
this.disableShadow(ctx);
}
return via;
}
}, {
key: 'findBorderPosition',
@ -33158,21 +33160,16 @@ return /******/ (function(modules) { // webpackBootstrap
* @param viaNode
*/
}, {
key: 'drawArrowHead',
value: function drawArrowHead(ctx, position, viaNode, selected, hover) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this.getLineWidth(selected, hover);
key: 'getArrowData',
value: function getArrowData(ctx, position, viaNode, selected, hover) {
// set lets
var angle = undefined;
var length = undefined;
var arrowPos = undefined;
var arrowPoint = undefined;
var node1 = undefined;
var node2 = undefined;
var guideOffset = undefined;
var scaleFactor = undefined;
var lineWidth = this.getLineWidth(selected, hover);
if (position === 'from') {
node1 = this.from;
@ -33195,64 +33192,70 @@ return /******/ (function(modules) { // webpackBootstrap
if (position !== 'middle') {
// draw arrow head
if (this.options.smooth.enabled === true) {
arrowPos = this.findBorderPosition(node1, ctx, { via: viaNode });
var guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPos.t + guideOffset)), viaNode);
angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x);
arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode });
var guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode);
angle = Math.atan2(arrowPoint.y - guidePos.y, arrowPoint.x - guidePos.x);
} else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
arrowPos = this.findBorderPosition(node1, ctx);
arrowPoint = this.findBorderPosition(node1, ctx);
}
} else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow.
arrowPoint = this.getPoint(0.5, 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);
} else {
var _getCircleData7 = this._getCircleData(ctx);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
var _getCircleData72 = _slicedToArray(_getCircleData7, 3);
// disable shadows for other elements.
this.disableShadow(ctx);
ctx.stroke();
} else {
// draw circle
var _angle = undefined,
point = undefined;
var x = _getCircleData72[0];
var y = _getCircleData72[1];
var radius = _getCircleData72[2];
var _getCircleData7 = this._getCircleData(ctx);
if (position === 'from') {
arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
} else if (position === 'to') {
arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1.0, direction: 1 });
angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
} else {
arrowPoint = this._pointOnCircle(x, y, radius, 0.175);
angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
}
var _getCircleData72 = _slicedToArray(_getCircleData7, 3);
var length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge.
var x = _getCircleData72[0];
var y = _getCircleData72[1];
var radius = _getCircleData72[2];
var xi = arrowPoint.x - length * 0.9 * Math.cos(angle);
var yi = arrowPoint.y - length * 0.9 * Math.sin(angle);
var arrowCore = { x: xi, y: yi };
if (position === 'from') {
point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
_angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
} else if (position === 'to') {
point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1.0, direction: 1 });
_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;
}
return { point: arrowPoint, core: arrowCore, angle: angle, length: length };
}
// draw the arrowhead
var _length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(point.x, point.y, _angle, _length);
/**
*
* @param ctx
* @param selected
* @param hover
* @param arrowData
*/
}, {
key: 'drawArrowHead',
value: function drawArrowHead(ctx, selected, hover, arrowData) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this.getLineWidth(selected, hover);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
// draw arrow at the end of the line
ctx.arrow(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
// disable shadows for other elements.
this.disableShadow(ctx);
ctx.stroke();
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
// disable shadows for other elements.
this.disableShadow(ctx);
}
}, {
key: 'enableShadow',
@ -33282,6 +33285,8 @@ return /******/ (function(modules) { // webpackBootstrap
exports['default'] = EdgeBase;
module.exports = exports['default'];
// draw circle
/***/ },
/* 86 */
/***/ function(module, exports, __webpack_require__) {
@ -33294,7 +33299,7 @@ return /******/ (function(modules) { // webpackBootstrap
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
@ -33423,31 +33428,42 @@ return /******/ (function(modules) { // webpackBootstrap
*/
}, {
key: "_line",
value: function _line(ctx) {
value: function _line(ctx, viaNode) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (viaNode.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
} else {
ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
}
}, {
key: "getViaNode",
value: function getViaNode() {
return this.via;
}
/**
* 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
* @param viaNode
* @returns {{x: number, y: number}}
* @private
*/
}, {
key: "getPoint",
value: function getPoint(percentage) {
var viaNode = arguments.length <= 1 || arguments[1] === undefined ? this.via : arguments[1];
var t = percentage;
var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x;
var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y;
var x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
var y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
return { x: x, y: y };
}
@ -33511,26 +33527,33 @@ return /******/ (function(modules) { // webpackBootstrap
_createClass(BezierEdgeStatic, [{
key: '_line',
value: function _line(ctx) {
value: function _line(ctx, viaNode) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
var via = this._getViaCoordinates();
var returnValue = via;
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (via.x === undefined) {
ctx.lineTo(this.to.x, this.to.y);
returnValue = undefined;
if (viaNode.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
} else {
ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y);
ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
return returnValue;
}
}, {
key: 'getViaNode',
value: function getViaNode() {
return this._getViaCoordinates();
}
/**
* We do not use the to and fromPoints here to make the via nodes the same as edges without arrows.
* @returns {{x: undefined, y: undefined}}
* @private
*/
}, {
key: '_getViaCoordinates',
value: function _getViaCoordinates() {
@ -33698,26 +33721,26 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: '_getDistanceToEdge',
value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
var via = arguments.length <= 6 || arguments[6] === undefined ? this._getViaCoordinates() : arguments[6];
var viaNode = arguments.length <= 6 || arguments[6] === undefined ? this._getViaCoordinates() : arguments[6];
// x3,y3 is the point
return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via);
return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode);
}
/**
* 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
* @param viaNode
* @returns {{x: number, y: number}}
* @private
*/
}, {
key: 'getPoint',
value: function getPoint(percentage) {
var via = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1];
var viaNode = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1];
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;
var x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
var y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
return { x: x, y: y };
}
@ -33773,12 +33796,16 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _line(ctx) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.lineTo(this.to.x, this.to.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
ctx.lineTo(this.toPoint.x, this.toPoint.y);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
}
}, {
key: 'getViaNode',
value: function getViaNode() {
return undefined;
}
@ -33793,8 +33820,8 @@ return /******/ (function(modules) { // webpackBootstrap
key: 'getPoint',
value: function getPoint(percentage) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
x: (1 - percentage) * this.fromPoint.x + percentage * this.toPoint.x,
y: (1 - percentage) * this.fromPoint.y + percentage * this.toPoint.y
};
}
}, {
@ -39980,7 +40007,8 @@ return /******/ (function(modules) { // webpackBootstrap
this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.distributionOrdering = {};
this.distributionOrderingPresence = {};
this.bindEventListeners();
}
@ -40016,7 +40044,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.body.emitter.emit('refresh', true);
}
// make sure the level seperation is the right way up
// make sure the level separation is the right way up
if (this.options.hierarchical.direction === 'RL' || this.options.hierarchical.direction === 'DU') {
if (this.options.hierarchical.levelSeparation > 0) {
this.options.hierarchical.levelSeparation *= -1;
@ -40128,14 +40156,14 @@ return /******/ (function(modules) { // webpackBootstrap
}
/**
* Use KamadaKawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
* Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
* cluster them first to reduce the amount.
*/
}, {
key: 'layoutNetwork',
value: function layoutNetwork() {
if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) {
// first check if we should KamadaKawai to layout. The threshold is if less than half of the visible
// first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible
// nodes have predefined positions we use this.
var positionDefined = 0;
for (var i = 0; i < this.body.nodeIndices.length; i++) {
@ -40271,7 +40299,7 @@ return /******/ (function(modules) { // webpackBootstrap
throw new Error('To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.');
return;
} else {
// define levels if undefined by the users. Based on hubsize
// define levels if undefined by the users. Based on hubsize.
if (undefinedLevel === true) {
if (this.options.hierarchical.sortMethod === 'hubsize') {
this._determineLevelsByHubsize();
@ -40301,12 +40329,21 @@ return /******/ (function(modules) { // webpackBootstrap
}
/**
* TODO: implement. Clear whitespace after positioning.
* @private
*/
}, {
key: '_condenseHierarchy',
value: function _condenseHierarchy(distribution) {}
value: function _condenseHierarchy(distribution) {
//console.log(this.distributionOrdering);
//let iterations = 10;
//for (let i = 0; i < iterations; i++) {
//}
}
}, {
key: '_removeWhiteSpace',
value: function _removeWhiteSpace(distribution) {}
/**
* This function places the nodes on the canvas based on the hierarchial distribution.
@ -40329,7 +40366,7 @@ return /******/ (function(modules) { // webpackBootstrap
for (var i = 0; i < nodeArray.length; i++) {
var node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) {
this._setPositionForHierarchy(node, this.nodeSpacing * i);
this._setPositionForHierarchy(node, this.nodeSpacing * i, level);
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
}
@ -40635,7 +40672,7 @@ return /******/ (function(modules) { // webpackBootstrap
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
var childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the childnode is below the parent node and if it has already been positioned.
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
var pos = undefined;
@ -40646,7 +40683,7 @@ return /******/ (function(modules) { // webpackBootstrap
} else {
pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.nodeSpacing;
}
this._setPositionForHierarchy(childNode, pos);
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
@ -40677,7 +40714,7 @@ return /******/ (function(modules) { // webpackBootstrap
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos));
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
/**
@ -40747,11 +40784,22 @@ return /******/ (function(modules) { // webpackBootstrap
* Abstract the getting of the position so we won't have to repeat the check for direction all the time
* @param node
* @param position
* @param level
* @private
*/
}, {
key: '_setPositionForHierarchy',
value: function _setPositionForHierarchy(node, position) {
value: function _setPositionForHierarchy(node, position, level) {
if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = [];
this.distributionOrderingPresence[level] = {};
}
if (this.distributionOrderingPresence[level][node.id] === undefined) {
this.distributionOrdering[level].push(node);
}
this.distributionOrderingPresence[level][node.id] = true;
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
node.x = position;
} else {
@ -42055,6 +42103,7 @@ return /******/ (function(modules) { // webpackBootstrap
from: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } },
__type__: { string: ['from', 'to', 'middle'], object: object }
},
arrowStrikethrough: { boolean: boolean },
color: {
color: { string: string },
highlight: { string: string },
@ -42372,6 +42421,7 @@ return /******/ (function(modules) { // webpackBootstrap
},
shadow: {
enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1],
x: [5, -30, 30, 1],
y: [5, -30, 30, 1]
@ -42390,6 +42440,7 @@ return /******/ (function(modules) { // webpackBootstrap
middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
},
arrowStrikethrough: false,
color: {
color: ['color', '#848484'],
highlight: ['color', '#848484'],
@ -42426,6 +42477,7 @@ return /******/ (function(modules) { // webpackBootstrap
selfReferenceSize: [20, 0, 200, 1],
shadow: {
enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1],
x: [5, -30, 30, 1],
y: [5, -30, 30, 1]

+ 7
- 0
docs/network/edges.html View File

@ -119,6 +119,7 @@ var options = {
middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1}
},
arrowsEnding: false,
color: {
color:'#848484',
highlight:'#848484',
@ -254,6 +255,12 @@ network.setOptions(options);
<td><code>Object</code></td>
<td>Exactly the same as the to object but with an arrowhead at the from node of the edge.</td>
</tr>
<tr>
<td class="indent">arrowStrikethrough</td>
<td>Boolean</td>
<td><code>false</code></td>
<td>When true, the edge stops at the arrow. This can be useful if you have thick lines and you want the arrow to end in a point. Middle arrows are not affected by this.</td>
</tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','color', this);">
<td><span parent="color" class="right-caret"></span> color</td>
<td>Object or String</td>

+ 1
- 0
lib/network/modules/EdgesHandler.js View File

@ -27,6 +27,7 @@ class EdgesHandler {
middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1}
},
arrowStrikethrough: false,
color: {
color:'#848484',
highlight:'#848484',

+ 33
- 14
lib/network/modules/LayoutEngine.js View File

@ -12,7 +12,6 @@ class LayoutEngine {
this.options = {};
this.optionsBackup = {};
this.defaultOptions = {
randomSeed: undefined,
improvedLayout: true,
@ -28,7 +27,8 @@ class LayoutEngine {
this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.distributionOrdering = {};
this.distributionOrderingPresence = {};
this.bindEventListeners();
}
@ -57,7 +57,7 @@ class LayoutEngine {
this.body.emitter.emit('refresh', true);
}
// make sure the level seperation is the right way up
// make sure the level separation is the right way up
if (this.options.hierarchical.direction === 'RL' || this.options.hierarchical.direction === 'DU') {
if (this.options.hierarchical.levelSeparation > 0) {
this.options.hierarchical.levelSeparation *= -1;
@ -174,12 +174,12 @@ class LayoutEngine {
/**
* Use KamadaKawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
* Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
* cluster them first to reduce the amount.
*/
layoutNetwork() {
if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) {
// first check if we should KamadaKawai to layout. The threshold is if less than half of the visible
// first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible
// nodes have predefined positions we use this.
let positionDefined = 0;
for (let i = 0; i < this.body.nodeIndices.length; i++) {
@ -311,7 +311,7 @@ class LayoutEngine {
return;
}
else {
// define levels if undefined by the users. Based on hubsize
// define levels if undefined by the users. Based on hubsize.
if (undefinedLevel === true) {
if (this.options.hierarchical.sortMethod === 'hubsize') {
this._determineLevelsByHubsize();
@ -344,13 +344,23 @@ class LayoutEngine {
}
/**
* TODO: implement. Clear whitespace after positioning.
* @private
*/
_condenseHierarchy(distribution) {
//console.log(this.distributionOrdering);
//let iterations = 10;
//for (let i = 0; i < iterations; i++) {
//}
}
_removeWhiteSpace(distribution) {
}
/**
* This function places the nodes on the canvas based on the hierarchial distribution.
*
@ -370,7 +380,7 @@ class LayoutEngine {
for (let i = 0; i < nodeArray.length; i++) {
let node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) {
this._setPositionForHierarchy(node, this.nodeSpacing * i);
this._setPositionForHierarchy(node, this.nodeSpacing * i, level);
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
}
@ -625,8 +635,6 @@ class LayoutEngine {
}
crawler(node);
}
}
@ -657,7 +665,7 @@ class LayoutEngine {
for (let i = 0; i < childNodes.length; i++) {
let childNode = childNodes[i];
let childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the childnode is below the parent node and if it has already been positioned.
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
let pos;
@ -665,7 +673,7 @@ class LayoutEngine {
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);}
else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.nodeSpacing;}
this._setPositionForHierarchy(childNode, pos);
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
@ -697,7 +705,7 @@ class LayoutEngine {
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos));
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
@ -764,9 +772,20 @@ class LayoutEngine {
* Abstract the getting of the position so we won't have to repeat the check for direction all the time
* @param node
* @param position
* @param level
* @private
*/
_setPositionForHierarchy(node, position) {
_setPositionForHierarchy(node, position, level) {
if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = [];
this.distributionOrderingPresence[level] = {};
}
if (this.distributionOrderingPresence[level][node.id] === undefined) {
this.distributionOrdering[level].push(node);
}
this.distributionOrderingPresence[level][node.id] = true;
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
node.x = position;
}

+ 36
- 7
lib/network/modules/components/Edge.js View File

@ -95,6 +95,7 @@ class Edge {
static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) {
var fields = [
'arrowStrikethrough',
'id',
'from',
'hidden',
@ -373,17 +374,45 @@ class Edge {
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx) {
let via = this.edgeType.drawLine(ctx, this.selected, this.hover);
this.drawArrows(ctx, via);
this.drawLabel (ctx, via);
// get the via node from the edge type
let viaNode = this.edgeType.getViaNode();
let arrowData = {};
// restore edge targets to defaults
this.edgeType.fromPoint = this.from;
this.edgeType.toPoint = this.to;
// from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) {
arrowData.from = this.edgeType.getArrowData(ctx,'from', viaNode, this.selected, this.hover);
if (this.options.arrowStrikethrough === true)
this.edgeType.fromPoint = arrowData.from.core;
}
if (this.options.arrows.to.enabled === true) {
arrowData.to = this.edgeType.getArrowData(ctx,'to', viaNode, this.selected, this.hover);
if (this.options.arrowStrikethrough === true)
this.edgeType.toPoint = arrowData.to.core;
}
// the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly.
if (this.options.arrows.middle.enabled === true) {
arrowData.middle = this.edgeType.getArrowData(ctx,'middle', viaNode, this.selected, this.hover);
}
// draw everything
this.edgeType.drawLine(ctx, this.selected, this.hover, viaNode);
this.drawArrows(ctx, arrowData);
this.drawLabel (ctx, viaNode);
}
drawArrows(ctx, viaNode) {
if (this.options.arrows.from.enabled === true) {this.edgeType.drawArrowHead(ctx,'from', viaNode, this.selected, this.hover);}
if (this.options.arrows.middle.enabled === true) {this.edgeType.drawArrowHead(ctx,'middle', viaNode, this.selected, this.hover);}
if (this.options.arrows.to.enabled === true) {this.edgeType.drawArrowHead(ctx,'to', viaNode, this.selected, this.hover);}
drawArrows(ctx, arrowData) {
if (this.options.arrows.from.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.from);}
if (this.options.arrows.middle.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.middle);}
if (this.options.arrows.to.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.to);}
}
drawLabel(ctx, viaNode) {
if (this.options.label !== undefined) {
// set style

+ 16
- 7
lib/network/modules/components/edges/BezierEdgeDynamic.js View File

@ -102,15 +102,24 @@ class BezierEdgeDynamic extends BezierEdgeBase {
* @param {CanvasRenderingContext2D} ctx
* @private
*/
_line(ctx) {
_line(ctx, viaNode) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (viaNode.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
}
else {
ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
}
getViaNode() {
return this.via;
}
@ -118,14 +127,14 @@ class BezierEdgeDynamic extends BezierEdgeBase {
/**
* 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
* @param viaNode
* @returns {{x: number, y: number}}
* @private
*/
getPoint(percentage) {
getPoint(percentage, viaNode = this.via) {
let t = percentage;
let x = Math.pow(1 - t, 2) * this.from.x + (2 * t * (1 - t)) * this.via.x + Math.pow(t, 2) * this.to.x;
let y = Math.pow(1 - t, 2) * this.from.y + (2 * t * (1 - t)) * this.via.y + Math.pow(t, 2) * this.to.y;
let x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
let y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
return {x: x, y: y};
}

+ 21
- 15
lib/network/modules/components/edges/BezierEdgeStatic.js View File

@ -10,28 +10,34 @@ class BezierEdgeStatic extends BezierEdgeBase {
* @param {CanvasRenderingContext2D} ctx
* @private
*/
_line(ctx) {
_line(ctx, viaNode) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
let via = this._getViaCoordinates();
let returnValue = via;
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (via.x === undefined) {
ctx.lineTo(this.to.x, this.to.y);
returnValue = undefined;
if (viaNode.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
}
else {
ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y);
ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
return returnValue;
}
getViaNode() {
return this._getViaCoordinates();
}
/**
* We do not use the to and fromPoints here to make the via nodes the same as edges without arrows.
* @returns {{x: undefined, y: undefined}}
* @private
*/
_getViaCoordinates() {
let xVia = undefined;
let yVia = undefined;
@ -214,21 +220,21 @@ class BezierEdgeStatic extends BezierEdgeBase {
return this._findBorderPositionBezier(nearNode, ctx, options.via);
}
_getDistanceToEdge(x1, y1, x2, y2, x3, y3, via = this._getViaCoordinates()) { // x3,y3 is the point
return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via);
_getDistanceToEdge(x1, y1, x2, y2, x3, y3, viaNode = this._getViaCoordinates()) { // x3,y3 is the point
return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode);
}
/**
* 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
* @param viaNode
* @returns {{x: number, y: number}}
* @private
*/
getPoint(percentage, via = this._getViaCoordinates()) {
getPoint(percentage, viaNode = this._getViaCoordinates()) {
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;
var x = Math.pow(1 - t, 2) * this.fromPoint.x + (2 * t * (1 - t)) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
var y = Math.pow(1 - t, 2) * this.fromPoint.y + (2 * t * (1 - t)) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
return {x: x, y: y};
}

+ 14
- 12
lib/network/modules/components/edges/CubicBezierEdge.js View File

@ -10,28 +10,26 @@ class CubicBezierEdge extends CubicBezierEdgeBase {
* @param {CanvasRenderingContext2D} ctx
* @private
*/
_line(ctx) {
_line(ctx, viaNodes) {
// get the coordinates of the support points.
let [via1,via2] = this._getViaCoordinates();
let returnValue = [via1,via2];
let via1 = viaNodes[0];
let via2 = viaNodes[1];
// start drawing the line.
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges
if (via1.x === undefined) {
ctx.lineTo(this.to.x, this.to.y);
returnValue = undefined;
if (viaNodes === undefined || via1.x === undefined) {
ctx.lineTo(this.toPoint.x, this.toPoint.y);
}
else {
ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.to.x, this.to.y);
ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.toPoint.x, this.toPoint.y);
}
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
return returnValue;
}
_getViaCoordinates() {
@ -39,7 +37,7 @@ class CubicBezierEdge extends CubicBezierEdgeBase {
let dy = this.from.y - this.to.y;
let x1, y1, x2, y2;
let roundness = this.options.smooth.roundness;;
let roundness = this.options.smooth.roundness;
// horizontal if x > y or if direction is forced or if direction is horizontal
if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') {
@ -58,6 +56,10 @@ class CubicBezierEdge extends CubicBezierEdgeBase {
return [{x: x1, y: y1},{x: x2, y: y2}];
}
getViaNode() {
return this._getViaCoordinates();
}
_findBorderPosition(nearNode, ctx) {
return this._findBorderPositionBezier(nearNode, ctx);
}
@ -80,8 +82,8 @@ class CubicBezierEdge extends CubicBezierEdgeBase {
vec[1] = 3 * t * Math.pow(1 - t, 2);
vec[2] = 3 * Math.pow(t,2) * (1 - t);
vec[3] = Math.pow(t, 3);
let x = vec[0] * this.from.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.to.x;
let y = vec[0] * this.from.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.to.y;
let x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x;
let y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y;
return {x: x, y: y};
}

+ 7
- 4
lib/network/modules/components/edges/StraightEdge.js View File

@ -13,12 +13,15 @@ class StraightEdge extends EdgeBase {
_line(ctx) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.lineTo(this.to.x, this.to.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
ctx.lineTo(this.toPoint.x, this.toPoint.y);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.stroke();
this.disableShadow(ctx);
}
getViaNode() {
return undefined;
}
@ -31,8 +34,8 @@ class StraightEdge extends EdgeBase {
*/
getPoint(percentage) {
return {
x: (1 - percentage) * this.from.x + percentage * this.to.x,
y: (1 - percentage) * this.from.y + percentage * this.to.y
x: (1 - percentage) * this.fromPoint.x + percentage * this.toPoint.x,
y: (1 - percentage) * this.fromPoint.y + percentage * this.toPoint.y
}
}

+ 51
- 51
lib/network/modules/components/edges/util/EdgeBase.js View File

@ -10,6 +10,8 @@ class EdgeBase {
this.color = {};
this.selectionWidth = 2;
this.hoverWidth = 1.5;
this.fromPoint = this.from;
this.toPoint = this.to;
}
connect() {
@ -32,36 +34,32 @@ class EdgeBase {
* @param {CanvasRenderingContext2D} ctx
* @private
*/
drawLine(ctx, selected, hover) {
drawLine(ctx, selected, hover, viaNode) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.lineWidth = this.getLineWidth(selected, hover);
let via = undefined;
if (this.options.dashes !== false) {
via = this._drawDashedLine(ctx);
this._drawDashedLine(ctx, viaNode);
}
else {
via = this._drawLine(ctx);
this._drawLine(ctx, viaNode);
}
return via;
}
_drawLine(ctx) {
let via = undefined;
_drawLine(ctx, viaNode, fromPoint, toPoint) {
if (this.from != this.to) {
// draw line
via = this._line(ctx);
this._line(ctx, viaNode, fromPoint, toPoint);
}
else {
let [x,y,radius] = this._getCircleData(ctx);
this._circle(ctx, x, y, radius);
}
return via;
}
_drawDashedLine(ctx) {
let via = undefined;
_drawDashedLine(ctx, viaNode, fromPoint, toPoint) {
ctx.lineCap = 'round';
let pattern = [5,5];
if (Array.isArray(this.options.dashes) === true) {
@ -79,7 +77,7 @@ class EdgeBase {
// draw the line
if (this.from != this.to) {
// draw line
via = this._line(ctx);
this._line(ctx, viaNode);
}
else {
let [x,y,radius] = this._getCircleData(ctx);
@ -108,7 +106,6 @@ class EdgeBase {
// disable shadows for other elements.
this.disableShadow(ctx);
}
return via;
}
@ -400,26 +397,22 @@ class EdgeBase {
return Math.sqrt(dx * dx + dy * dy);
}
/**
*
* @param ctx
* @param position
* @param viaNode
*/
drawArrowHead(ctx, position, viaNode, selected, hover) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this.getLineWidth(selected, hover);
getArrowData(ctx, position, viaNode, selected, hover) {
// set lets
let angle;
let length;
let arrowPos;
let arrowPoint;
let node1;
let node2;
let guideOffset;
let scaleFactor;
let lineWidth = this.getLineWidth(selected, hover);
if (position === 'from') {
node1 = this.from;
@ -444,61 +437,68 @@ class EdgeBase {
if (position !== 'middle') {
// draw arrow head
if (this.options.smooth.enabled === true) {
arrowPos = this.findBorderPosition(node1, ctx, {via: viaNode});
let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPos.t + guideOffset)), viaNode);
angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x));
arrowPoint = this.findBorderPosition(node1, ctx, {via: viaNode});
let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode);
angle = Math.atan2((arrowPoint.y - guidePos.y), (arrowPoint.x - guidePos.x));
}
else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
arrowPos = this.findBorderPosition(node1, ctx);
arrowPoint = this.findBorderPosition(node1, ctx);
}
}
else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow.
arrowPoint = this.getPoint(0.5, 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);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
// disable shadows for other elements.
this.disableShadow(ctx);
ctx.stroke();
}
else {
// draw circle
let angle, point;
let [x,y,radius] = this._getCircleData(ctx);
if (position === 'from') {
point = this.findBorderPosition(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1});
arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1});
angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
else if (position === 'to') {
point = this.findBorderPosition(this.from, ctx, {x, y, low:0.6, high:1.0, direction:1});
arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.6, high:1.0, direction:1});
angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
}
else {
point = this._pointOnCircle(x, y, radius, 0.175);
arrowPoint = this._pointOnCircle(x, y, radius, 0.175);
angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
}
}
// draw the arrowhead
let length = (10 + 5 * this.options.width) * scaleFactor;
ctx.arrow(point.x, point.y, angle, length);
let length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge.
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
var xi = arrowPoint.x - length * 0.9 * Math.cos(angle);
var yi = arrowPoint.y - length * 0.9 * Math.sin(angle);
let arrowCore = {x: xi, y: yi};
// disable shadows for other elements.
this.disableShadow(ctx);
ctx.stroke();
}
return {point: arrowPoint, core: arrowCore, angle: angle, length: length};
}
/**
*
* @param ctx
* @param selected
* @param hover
* @param arrowData
*/
drawArrowHead(ctx, selected, hover, arrowData) {
// set style
ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = this.getLineWidth(selected, hover);
// draw arrow at the end of the line
ctx.arrow(arrowData.point.x, arrowData.point.y, arrowData.angle, arrowData.length);
// draw shadow if enabled
this.enableShadow(ctx);
ctx.fill();
// disable shadows for other elements.
this.disableShadow(ctx);
}

+ 1
- 1
lib/network/modules/components/nodes/shapes/Dot.js View File

@ -17,7 +17,7 @@ class Dot extends ShapeBase {
distanceToBorder(ctx, angle) {
this.resize(ctx);
return this.options.size + this.options.borderWidth;
return this.options.size;
}
}

+ 4
- 0
lib/network/options.js View File

@ -29,6 +29,7 @@ let allOptions = {
from: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } },
__type__: { string: ['from', 'to', 'middle'], object }
},
arrowStrikethrough: { boolean },
color: {
color: { string },
highlight: { string },
@ -347,6 +348,7 @@ let configureOptions = {
},
shadow: {
enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1],
x: [5, -30, 30, 1],
y: [5, -30, 30, 1]
@ -365,6 +367,7 @@ let configureOptions = {
middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
},
arrowStrikethrough: false,
color: {
color: ['color', '#848484'],
highlight: ['color', '#848484'],
@ -401,6 +404,7 @@ let configureOptions = {
selfReferenceSize: [20, 0, 200, 1],
shadow: {
enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1],
x: [5, -30, 30, 1],
y: [5, -30, 30, 1]

+ 33
- 53
test/networkTest.html
File diff suppressed because it is too large
View File


Loading…
Cancel
Save