Browse Source

- Altered edges for arrows and added the arrowStrikethrough option.

codeClimate
Alex de Mulder 8 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 ## 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 ## 2016-01-08, version 4.12.0

+ 211
- 159
dist/vis.js View File

@ -4,8 +4,8 @@
* *
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 4.12.0
* @date 2016-01-08
* @version 4.12.1-SNAPSHOT
* @date 2016-01-11
* *
* @license * @license
* Copyright (C) 2011-2016 Almende B.V, http://almende.com * Copyright (C) 2011-2016 Almende B.V, http://almende.com
@ -11047,14 +11047,14 @@ return /******/ (function(modules) { // webpackBootstrap
var PropagatingHammer = function(element, options) { var PropagatingHammer = function(element, options) {
var o = Object.create(_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); return propagating(new Hammer(element, o), o);
}; };
Hammer.assign(PropagatingHammer, Hammer);
Hammer.extend(PropagatingHammer, Hammer);
PropagatingHammer.Manager = function (element, options) { PropagatingHammer.Manager = function (element, options) {
var o = Object.create(_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); return propagating(new Hammer.Manager(element, o), o);
}; };
@ -11067,9 +11067,7 @@ return /******/ (function(modules) { // webpackBootstrap
// attach to DOM element // attach to DOM element
var element = hammer.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 // register an event to catch the start of a gesture and store the
// target in a singleton // target in a singleton
@ -11150,10 +11148,7 @@ return /******/ (function(modules) { // webpackBootstrap
wrapper.destroy = function () { wrapper.destroy = function () {
// Detach from DOM element // 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 // clear all handlers
wrapper._handlers = {}; wrapper._handlers = {};
@ -11194,30 +11189,19 @@ return /******/ (function(modules) { // webpackBootstrap
stopped = true; 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 // attach firstTarget property to the event
event.firstTarget = _firstTarget; event.firstTarget = _firstTarget;
// propagate over all elements (until stopped) // propagate over all elements (until stopped)
var elem = _firstTarget; var elem = _firstTarget;
while (elem && !stopped) { 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; elem = elem.parentNode;
} }
} }
@ -30631,7 +30615,7 @@ return /******/ (function(modules) { // webpackBootstrap
key: 'distanceToBorder', key: 'distanceToBorder',
value: function distanceToBorder(ctx, angle) { value: function distanceToBorder(ctx, angle) {
this.resize(ctx); 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 }, middle: { enabled: false, scaleFactor: 1 },
from: { enabled: false, scaleFactor: 1 } from: { enabled: false, scaleFactor: 1 }
}, },
arrowStrikethrough: false,
color: { color: {
color: '#848484', color: '#848484',
highlight: '#848484', highlight: '#848484',
@ -32063,21 +32048,45 @@ return /******/ (function(modules) { // webpackBootstrap
}, { }, {
key: 'draw', key: 'draw',
value: function draw(ctx) { 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', key: 'drawArrows',
value: function drawArrows(ctx, viaNode) {
value: function drawArrows(ctx, arrowData) {
if (this.options.arrows.from.enabled === true) { 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) { 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) { 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 allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; 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. // only deep extend the items in the field array. These do not have shorthand.
util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion);
@ -32354,34 +32363,25 @@ return /******/ (function(modules) { // webpackBootstrap
_createClass(CubicBezierEdge, [{ _createClass(CubicBezierEdge, [{
key: '_line', key: '_line',
value: function _line(ctx) {
value: function _line(ctx, viaNodes) {
// get the coordinates of the support points. // 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. // start drawing the line.
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges // 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 { } 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
return returnValue;
} }
}, { }, {
key: '_getViaCoordinates', key: '_getViaCoordinates',
@ -32393,7 +32393,7 @@ return /******/ (function(modules) { // webpackBootstrap
y1 = undefined, y1 = undefined,
x2 = undefined, x2 = undefined,
y2 = 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 // 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') { 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 }]; return [{ x: x1, y: y1 }, { x: x2, y: y2 }];
} }
}, {
key: 'getViaNode',
value: function getViaNode() {
return this._getViaCoordinates();
}
}, { }, {
key: '_findBorderPosition', key: '_findBorderPosition',
value: function _findBorderPosition(nearNode, ctx) { value: function _findBorderPosition(nearNode, ctx) {
@ -32451,8 +32456,8 @@ return /******/ (function(modules) { // webpackBootstrap
vec[1] = 3 * t * Math.pow(1 - t, 2); vec[1] = 3 * t * Math.pow(1 - t, 2);
vec[2] = 3 * Math.pow(t, 2) * (1 - t); vec[2] = 3 * Math.pow(t, 2) * (1 - t);
vec[3] = Math.pow(t, 3); 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 }; return { x: x, y: y };
} }
@ -32723,6 +32728,8 @@ return /******/ (function(modules) { // webpackBootstrap
this.color = {}; this.color = {};
this.selectionWidth = 2; this.selectionWidth = 2;
this.hoverWidth = 1.5; this.hoverWidth = 1.5;
this.fromPoint = this.from;
this.toPoint = this.to;
} }
_createClass(EdgeBase, [{ _createClass(EdgeBase, [{
@ -32754,25 +32761,23 @@ return /******/ (function(modules) { // webpackBootstrap
*/ */
}, { }, {
key: 'drawLine', key: 'drawLine',
value: function drawLine(ctx, selected, hover) {
value: function drawLine(ctx, selected, hover, viaNode) {
// set style // set style
ctx.strokeStyle = this.getColor(ctx, selected, hover); ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.lineWidth = this.getLineWidth(selected, hover); ctx.lineWidth = this.getLineWidth(selected, hover);
var via = undefined;
if (this.options.dashes !== false) { if (this.options.dashes !== false) {
via = this._drawDashedLine(ctx);
this._drawDashedLine(ctx, viaNode);
} else { } else {
via = this._drawLine(ctx);
this._drawLine(ctx, viaNode);
} }
return via;
} }
}, { }, {
key: '_drawLine', key: '_drawLine',
value: function _drawLine(ctx) {
var via = undefined;
value: function _drawLine(ctx, viaNode, fromPoint, toPoint) {
if (this.from != this.to) { if (this.from != this.to) {
// draw line // draw line
via = this._line(ctx);
this._line(ctx, viaNode, fromPoint, toPoint);
} else { } else {
var _getCircleData2 = this._getCircleData(ctx); var _getCircleData2 = this._getCircleData(ctx);
@ -32784,12 +32789,10 @@ return /******/ (function(modules) { // webpackBootstrap
this._circle(ctx, x, y, radius); this._circle(ctx, x, y, radius);
} }
return via;
} }
}, { }, {
key: '_drawDashedLine', key: '_drawDashedLine',
value: function _drawDashedLine(ctx) {
var via = undefined;
value: function _drawDashedLine(ctx, viaNode, fromPoint, toPoint) {
ctx.lineCap = 'round'; ctx.lineCap = 'round';
var pattern = [5, 5]; var pattern = [5, 5];
if (Array.isArray(this.options.dashes) === true) { if (Array.isArray(this.options.dashes) === true) {
@ -32807,7 +32810,7 @@ return /******/ (function(modules) { // webpackBootstrap
// draw the line // draw the line
if (this.from != this.to) { if (this.from != this.to) {
// draw line // draw line
via = this._line(ctx);
this._line(ctx, viaNode);
} else { } else {
var _getCircleData3 = this._getCircleData(ctx); var _getCircleData3 = this._getCircleData(ctx);
@ -32848,7 +32851,6 @@ return /******/ (function(modules) { // webpackBootstrap
// disable shadows for other elements. // disable shadows for other elements.
this.disableShadow(ctx); this.disableShadow(ctx);
} }
return via;
} }
}, { }, {
key: 'findBorderPosition', key: 'findBorderPosition',
@ -33158,21 +33160,16 @@ return /******/ (function(modules) { // webpackBootstrap
* @param viaNode * @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 // set lets
var angle = undefined; var angle = undefined;
var length = undefined;
var arrowPos = undefined;
var arrowPoint = undefined;
var node1 = undefined; var node1 = undefined;
var node2 = undefined; var node2 = undefined;
var guideOffset = undefined; var guideOffset = undefined;
var scaleFactor = undefined; var scaleFactor = undefined;
var lineWidth = this.getLineWidth(selected, hover);
if (position === 'from') { if (position === 'from') {
node1 = this.from; node1 = this.from;
@ -33195,64 +33192,70 @@ return /******/ (function(modules) { // webpackBootstrap
if (position !== 'middle') { if (position !== 'middle') {
// draw arrow head // draw arrow head
if (this.options.smooth.enabled === true) { 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 { } else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
arrowPos = this.findBorderPosition(node1, ctx);
arrowPoint = this.findBorderPosition(node1, ctx);
} }
} else { } else {
angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); 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', key: 'enableShadow',
@ -33282,6 +33285,8 @@ return /******/ (function(modules) { // webpackBootstrap
exports['default'] = EdgeBase; exports['default'] = EdgeBase;
module.exports = exports['default']; module.exports = exports['default'];
// draw circle
/***/ }, /***/ },
/* 86 */ /* 86 */
/***/ function(module, exports, __webpack_require__) { /***/ 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 _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 }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
@ -33423,31 +33428,42 @@ return /******/ (function(modules) { // webpackBootstrap
*/ */
}, { }, {
key: "_line", key: "_line",
value: function _line(ctx) {
value: function _line(ctx, viaNode) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
}
}, {
key: "getViaNode",
value: function getViaNode() {
return this.via; 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 * 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 percentage
* @param via
* @param viaNode
* @returns {{x: number, y: number}} * @returns {{x: number, y: number}}
* @private * @private
*/ */
}, { }, {
key: "getPoint", key: "getPoint",
value: function getPoint(percentage) { value: function getPoint(percentage) {
var viaNode = arguments.length <= 1 || arguments[1] === undefined ? this.via : arguments[1];
var t = percentage; 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 }; return { x: x, y: y };
} }
@ -33511,26 +33527,33 @@ return /******/ (function(modules) { // webpackBootstrap
_createClass(BezierEdgeStatic, [{ _createClass(BezierEdgeStatic, [{
key: '_line', key: '_line',
value: function _line(ctx) {
value: function _line(ctx, viaNode) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // 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 { } 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); 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', key: '_getViaCoordinates',
value: function _getViaCoordinates() { value: function _getViaCoordinates() {
@ -33698,26 +33721,26 @@ return /******/ (function(modules) { // webpackBootstrap
}, { }, {
key: '_getDistanceToEdge', key: '_getDistanceToEdge',
value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { 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 // 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 * 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 percentage
* @param via
* @param viaNode
* @returns {{x: number, y: number}} * @returns {{x: number, y: number}}
* @private * @private
*/ */
}, { }, {
key: 'getPoint', key: 'getPoint',
value: function getPoint(percentage) { 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 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 }; return { x: x, y: y };
} }
@ -33773,12 +33796,16 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _line(ctx) { value: function _line(ctx) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
}
}, {
key: 'getViaNode',
value: function getViaNode() {
return undefined; return undefined;
} }
@ -33793,8 +33820,8 @@ return /******/ (function(modules) { // webpackBootstrap
key: 'getPoint', key: 'getPoint',
value: function getPoint(percentage) { value: function getPoint(percentage) {
return { 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.lastNodeOnLevel = {};
this.hierarchicalParents = {}; this.hierarchicalParents = {};
this.hierarchicalChildren = {}; this.hierarchicalChildren = {};
this.distributionOrdering = {};
this.distributionOrderingPresence = {};
this.bindEventListeners(); this.bindEventListeners();
} }
@ -40016,7 +40044,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.body.emitter.emit('refresh', true); 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.direction === 'RL' || this.options.hierarchical.direction === 'DU') {
if (this.options.hierarchical.levelSeparation > 0) { if (this.options.hierarchical.levelSeparation > 0) {
this.options.hierarchical.levelSeparation *= -1; 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. * cluster them first to reduce the amount.
*/ */
}, { }, {
key: 'layoutNetwork', key: 'layoutNetwork',
value: function layoutNetwork() { value: function layoutNetwork() {
if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { 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. // nodes have predefined positions we use this.
var positionDefined = 0; var positionDefined = 0;
for (var i = 0; i < this.body.nodeIndices.length; i++) { 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.'); throw new Error('To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.');
return; return;
} else { } 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 (undefinedLevel === true) {
if (this.options.hierarchical.sortMethod === 'hubsize') { if (this.options.hierarchical.sortMethod === 'hubsize') {
this._determineLevelsByHubsize(); this._determineLevelsByHubsize();
@ -40301,12 +40329,21 @@ return /******/ (function(modules) { // webpackBootstrap
} }
/** /**
* TODO: implement. Clear whitespace after positioning.
* @private * @private
*/ */
}, { }, {
key: '_condenseHierarchy', 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. * 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++) { for (var i = 0; i < nodeArray.length; i++) {
var node = nodeArray[i]; var node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) { 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.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level); this._placeBranchNodes(node.id, level);
} }
@ -40635,7 +40672,7 @@ return /******/ (function(modules) { // webpackBootstrap
for (var i = 0; i < childNodes.length; i++) { for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i]; var childNode = childNodes[i];
var childNodeLevel = this.hierarchicalLevels[childNode.id]; 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) { 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. // get the amount of space required for this node. If parent the width is based on the amount of children.
var pos = undefined; var pos = undefined;
@ -40646,7 +40683,7 @@ return /******/ (function(modules) { // webpackBootstrap
} else { } else {
pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.nodeSpacing; 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 overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
@ -40677,7 +40714,7 @@ return /******/ (function(modules) { // webpackBootstrap
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, 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 * Abstract the getting of the position so we won't have to repeat the check for direction all the time
* @param node * @param node
* @param position * @param position
* @param level
* @private * @private
*/ */
}, { }, {
key: '_setPositionForHierarchy', 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') { if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
node.x = position; node.x = position;
} else { } else {
@ -42055,6 +42103,7 @@ return /******/ (function(modules) { // webpackBootstrap
from: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } }, from: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } },
__type__: { string: ['from', 'to', 'middle'], object: object } __type__: { string: ['from', 'to', 'middle'], object: object }
}, },
arrowStrikethrough: { boolean: boolean },
color: { color: {
color: { string: string }, color: { string: string },
highlight: { string: string }, highlight: { string: string },
@ -42372,6 +42421,7 @@ return /******/ (function(modules) { // webpackBootstrap
}, },
shadow: { shadow: {
enabled: false, enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1], size: [10, 0, 20, 1],
x: [5, -30, 30, 1], x: [5, -30, 30, 1],
y: [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] }, middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] } from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
}, },
arrowStrikethrough: false,
color: { color: {
color: ['color', '#848484'], color: ['color', '#848484'],
highlight: ['color', '#848484'], highlight: ['color', '#848484'],
@ -42426,6 +42477,7 @@ return /******/ (function(modules) { // webpackBootstrap
selfReferenceSize: [20, 0, 200, 1], selfReferenceSize: [20, 0, 200, 1],
shadow: { shadow: {
enabled: false, enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1], size: [10, 0, 20, 1],
x: [5, -30, 30, 1], x: [5, -30, 30, 1],
y: [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}, middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1} from: {enabled: false, scaleFactor:1}
}, },
arrowsEnding: false,
color: { color: {
color:'#848484', color:'#848484',
highlight:'#848484', highlight:'#848484',
@ -254,6 +255,12 @@ network.setOptions(options);
<td><code>Object</code></td> <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> <td>Exactly the same as the to object but with an arrowhead at the from node of the edge.</td>
</tr> </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);"> <tr class='toggle collapsible' onclick="toggleTable('optionTable','color', this);">
<td><span parent="color" class="right-caret"></span> color</td> <td><span parent="color" class="right-caret"></span> color</td>
<td>Object or String</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}, middle: {enabled: false, scaleFactor:1},
from: {enabled: false, scaleFactor:1} from: {enabled: false, scaleFactor:1}
}, },
arrowStrikethrough: false,
color: { color: {
color:'#848484', color:'#848484',
highlight:'#848484', highlight:'#848484',

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

@ -12,7 +12,6 @@ class LayoutEngine {
this.options = {}; this.options = {};
this.optionsBackup = {}; this.optionsBackup = {};
this.defaultOptions = { this.defaultOptions = {
randomSeed: undefined, randomSeed: undefined,
improvedLayout: true, improvedLayout: true,
@ -28,7 +27,8 @@ class LayoutEngine {
this.lastNodeOnLevel = {}; this.lastNodeOnLevel = {};
this.hierarchicalParents = {}; this.hierarchicalParents = {};
this.hierarchicalChildren = {}; this.hierarchicalChildren = {};
this.distributionOrdering = {};
this.distributionOrderingPresence = {};
this.bindEventListeners(); this.bindEventListeners();
} }
@ -57,7 +57,7 @@ class LayoutEngine {
this.body.emitter.emit('refresh', true); 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.direction === 'RL' || this.options.hierarchical.direction === 'DU') {
if (this.options.hierarchical.levelSeparation > 0) { if (this.options.hierarchical.levelSeparation > 0) {
this.options.hierarchical.levelSeparation *= -1; 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. * cluster them first to reduce the amount.
*/ */
layoutNetwork() { layoutNetwork() {
if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { 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. // nodes have predefined positions we use this.
let positionDefined = 0; let positionDefined = 0;
for (let i = 0; i < this.body.nodeIndices.length; i++) { for (let i = 0; i < this.body.nodeIndices.length; i++) {
@ -311,7 +311,7 @@ class LayoutEngine {
return; return;
} }
else { 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 (undefinedLevel === true) {
if (this.options.hierarchical.sortMethod === 'hubsize') { if (this.options.hierarchical.sortMethod === 'hubsize') {
this._determineLevelsByHubsize(); this._determineLevelsByHubsize();
@ -344,13 +344,23 @@ class LayoutEngine {
} }
/** /**
* TODO: implement. Clear whitespace after positioning.
* @private * @private
*/ */
_condenseHierarchy(distribution) { _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. * 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++) { for (let i = 0; i < nodeArray.length; i++) {
let node = nodeArray[i]; let node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) { 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.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level); this._placeBranchNodes(node.id, level);
} }
@ -625,8 +635,6 @@ class LayoutEngine {
} }
crawler(node); crawler(node);
} }
} }
@ -657,7 +665,7 @@ class LayoutEngine {
for (let i = 0; i < childNodes.length; i++) { for (let i = 0; i < childNodes.length; i++) {
let childNode = childNodes[i]; let childNode = childNodes[i];
let childNodeLevel = this.hierarchicalLevels[childNode.id]; 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) { 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. // get the amount of space required for this node. If parent the width is based on the amount of children.
let pos; 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 // 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]);} if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);}
else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.nodeSpacing;} 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 overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) { if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
@ -697,7 +705,7 @@ class LayoutEngine {
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId])); minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, 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 * Abstract the getting of the position so we won't have to repeat the check for direction all the time
* @param node * @param node
* @param position * @param position
* @param level
* @private * @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') { if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
node.x = position; 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 = {}) { static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) {
var fields = [ var fields = [
'arrowStrikethrough',
'id', 'id',
'from', 'from',
'hidden', 'hidden',
@ -373,17 +374,45 @@ class Edge {
* @param {CanvasRenderingContext2D} ctx * @param {CanvasRenderingContext2D} ctx
*/ */
draw(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) { drawLabel(ctx, viaNode) {
if (this.options.label !== undefined) { if (this.options.label !== undefined) {
// set style // set style

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

@ -102,15 +102,24 @@ class BezierEdgeDynamic extends BezierEdgeBase {
* @param {CanvasRenderingContext2D} ctx * @param {CanvasRenderingContext2D} ctx
* @private * @private
*/ */
_line(ctx) {
_line(ctx, viaNode) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
}
getViaNode() {
return this.via; 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 * 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 percentage
* @param via
* @param viaNode
* @returns {{x: number, y: number}} * @returns {{x: number, y: number}}
* @private * @private
*/ */
getPoint(percentage) {
getPoint(percentage, viaNode = this.via) {
let t = percentage; 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}; 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 * @param {CanvasRenderingContext2D} ctx
* @private * @private
*/ */
_line(ctx) {
_line(ctx, viaNode) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // 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 { 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); 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() { _getViaCoordinates() {
let xVia = undefined; let xVia = undefined;
let yVia = undefined; let yVia = undefined;
@ -214,21 +220,21 @@ class BezierEdgeStatic extends BezierEdgeBase {
return this._findBorderPositionBezier(nearNode, ctx, options.via); 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 * 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 percentage
* @param via
* @param viaNode
* @returns {{x: number, y: number}} * @returns {{x: number, y: number}}
* @private * @private
*/ */
getPoint(percentage, via = this._getViaCoordinates()) {
getPoint(percentage, viaNode = this._getViaCoordinates()) {
var t = percentage; 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}; 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 * @param {CanvasRenderingContext2D} ctx
* @private * @private
*/ */
_line(ctx) {
_line(ctx, viaNodes) {
// get the coordinates of the support points. // 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. // start drawing the line.
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
// fallback to normal straight edges // 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 { 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
return returnValue;
} }
_getViaCoordinates() { _getViaCoordinates() {
@ -39,7 +37,7 @@ class CubicBezierEdge extends CubicBezierEdgeBase {
let dy = this.from.y - this.to.y; let dy = this.from.y - this.to.y;
let x1, y1, x2, y2; 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 // 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') { 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}]; return [{x: x1, y: y1},{x: x2, y: y2}];
} }
getViaNode() {
return this._getViaCoordinates();
}
_findBorderPosition(nearNode, ctx) { _findBorderPosition(nearNode, ctx) {
return this._findBorderPositionBezier(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[1] = 3 * t * Math.pow(1 - t, 2);
vec[2] = 3 * Math.pow(t,2) * (1 - t); vec[2] = 3 * Math.pow(t,2) * (1 - t);
vec[3] = Math.pow(t, 3); 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}; 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) { _line(ctx) {
// draw a straight line // draw a straight line
ctx.beginPath(); 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 // draw shadow if enabled
this.enableShadow(ctx); this.enableShadow(ctx);
ctx.stroke(); ctx.stroke();
this.disableShadow(ctx); this.disableShadow(ctx);
}
getViaNode() {
return undefined; return undefined;
} }
@ -31,8 +34,8 @@ class StraightEdge extends EdgeBase {
*/ */
getPoint(percentage) { getPoint(percentage) {
return { 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.color = {};
this.selectionWidth = 2; this.selectionWidth = 2;
this.hoverWidth = 1.5; this.hoverWidth = 1.5;
this.fromPoint = this.from;
this.toPoint = this.to;
} }
connect() { connect() {
@ -32,36 +34,32 @@ class EdgeBase {
* @param {CanvasRenderingContext2D} ctx * @param {CanvasRenderingContext2D} ctx
* @private * @private
*/ */
drawLine(ctx, selected, hover) {
drawLine(ctx, selected, hover, viaNode) {
// set style // set style
ctx.strokeStyle = this.getColor(ctx, selected, hover); ctx.strokeStyle = this.getColor(ctx, selected, hover);
ctx.lineWidth = this.getLineWidth(selected, hover); ctx.lineWidth = this.getLineWidth(selected, hover);
let via = undefined;
if (this.options.dashes !== false) { if (this.options.dashes !== false) {
via = this._drawDashedLine(ctx);
this._drawDashedLine(ctx, viaNode);
} }
else { 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) { if (this.from != this.to) {
// draw line // draw line
via = this._line(ctx);
this._line(ctx, viaNode, fromPoint, toPoint);
} }
else { else {
let [x,y,radius] = this._getCircleData(ctx); let [x,y,radius] = this._getCircleData(ctx);
this._circle(ctx, x, y, radius); this._circle(ctx, x, y, radius);
} }
return via;
} }
_drawDashedLine(ctx) {
let via = undefined;
_drawDashedLine(ctx, viaNode, fromPoint, toPoint) {
ctx.lineCap = 'round'; ctx.lineCap = 'round';
let pattern = [5,5]; let pattern = [5,5];
if (Array.isArray(this.options.dashes) === true) { if (Array.isArray(this.options.dashes) === true) {
@ -79,7 +77,7 @@ class EdgeBase {
// draw the line // draw the line
if (this.from != this.to) { if (this.from != this.to) {
// draw line // draw line
via = this._line(ctx);
this._line(ctx, viaNode);
} }
else { else {
let [x,y,radius] = this._getCircleData(ctx); let [x,y,radius] = this._getCircleData(ctx);
@ -108,7 +106,6 @@ class EdgeBase {
// disable shadows for other elements. // disable shadows for other elements.
this.disableShadow(ctx); this.disableShadow(ctx);
} }
return via;
} }
@ -400,26 +397,22 @@ class EdgeBase {
return Math.sqrt(dx * dx + dy * dy); return Math.sqrt(dx * dx + dy * dy);
} }
/** /**
* *
* @param ctx * @param ctx
* @param position * @param position
* @param viaNode * @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 // set lets
let angle; let angle;
let length;
let arrowPos;
let arrowPoint;
let node1; let node1;
let node2; let node2;
let guideOffset; let guideOffset;
let scaleFactor; let scaleFactor;
let lineWidth = this.getLineWidth(selected, hover);
if (position === 'from') { if (position === 'from') {
node1 = this.from; node1 = this.from;
@ -444,61 +437,68 @@ class EdgeBase {
if (position !== 'middle') { if (position !== 'middle') {
// draw arrow head // draw arrow head
if (this.options.smooth.enabled === true) { 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 { else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x));
arrowPos = this.findBorderPosition(node1, ctx);
arrowPoint = this.findBorderPosition(node1, ctx);
} }
} }
else { else {
angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); 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 { else {
// draw circle // draw circle
let angle, point;
let [x,y,radius] = this._getCircleData(ctx); let [x,y,radius] = this._getCircleData(ctx);
if (position === 'from') { 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; angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
} }
else if (position === 'to') { 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; angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
} }
else { 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; 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) { distanceToBorder(ctx, angle) {
this.resize(ctx); 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 } }, from: { enabled: { boolean }, scaleFactor: { number }, __type__: { object, boolean } },
__type__: { string: ['from', 'to', 'middle'], object } __type__: { string: ['from', 'to', 'middle'], object }
}, },
arrowStrikethrough: { boolean },
color: { color: {
color: { string }, color: { string },
highlight: { string }, highlight: { string },
@ -347,6 +348,7 @@ let configureOptions = {
}, },
shadow: { shadow: {
enabled: false, enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1], size: [10, 0, 20, 1],
x: [5, -30, 30, 1], x: [5, -30, 30, 1],
y: [5, -30, 30, 1] y: [5, -30, 30, 1]
@ -365,6 +367,7 @@ let configureOptions = {
middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }, middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] } from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
}, },
arrowStrikethrough: false,
color: { color: {
color: ['color', '#848484'], color: ['color', '#848484'],
highlight: ['color', '#848484'], highlight: ['color', '#848484'],
@ -401,6 +404,7 @@ let configureOptions = {
selfReferenceSize: [20, 0, 200, 1], selfReferenceSize: [20, 0, 200, 1],
shadow: { shadow: {
enabled: false, enabled: false,
color: 'rgba(0,0,0,0.5)',
size: [10, 0, 20, 1], size: [10, 0, 20, 1],
x: [5, -30, 30, 1], x: [5, -30, 30, 1],
y: [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