Browse Source

Added multiple smoothCurve methods, added hideNodesOnDrag and hideEdgesOnDrag options. Added two examples. Added inheritColor option.

v3_develop
Alex de Mulder 10 years ago
parent
commit
eaef289544
13 changed files with 10620 additions and 103 deletions
  1. +6
    -0
      HISTORY.md
  2. +54
    -3
      docs/network.html
  3. +5
    -5
      examples/network/02_random_nodes.html
  4. +1
    -0
      examples/network/23_hierarchical_layout.html
  5. +34
    -1
      examples/network/24_hierarchical_layout_userdefined.html
  6. +75
    -0
      examples/network/26_staticSmoothCurves.html
  7. +10089
    -0
      examples/network/27_world_cup_network.html
  8. +258
    -50
      lib/network/Edge.js
  9. +10
    -10
      lib/network/Groups.js
  10. +54
    -19
      lib/network/Network.js
  11. +14
    -1
      lib/network/mixins/HierarchicalLayoutMixin.js
  12. +3
    -0
      lib/network/mixins/ManipulationMixin.js
  13. +17
    -14
      lib/network/mixins/physics/PhysicsMixin.js

+ 6
- 0
HISTORY.md View File

@ -12,6 +12,12 @@ http://visjs.org
- Fixed range where the `end` of the first is equal to the `start` of the second
sometimes being stacked instead of put besides each other when `item.margin=0`.
### Network (formerly named Graph)
- Expanded smoothCurves options for improved support for large clusters.
- Added multiple types of smoothCurve drawing for greatly improved performance.
- Option for inherited edge colors from connected nodes.
- Option to disable the drawing of nodes or edges on drag.
## 2014-07-07, version 3.0.0

+ 54
- 3
docs/network.html View File

@ -672,6 +672,14 @@ When using a DataSet, the network is automatically updating to changes in the Da
<td>physics.[method].springLength</td>
<td>The resting length of the edge when modeled as a spring. By default the springLength determined by the physics is used. By using this setting you can make certain edges have different resting lengths.</td>
</tr>
<tr>
<td>inheritColor</td>
<td>String | Boolean</td>
<td>false</td>
<td>Possible values: <code>"to","from", true, false</code>. If this value is set to false, the edge color information is used. If the value is set to true or "from",
the color data from the borders of the "from" node is used. If this value is "to", the color data from the borders of the "to" node is used.</td>
</tr>
<tr>
<td>title</td>
<td>string | function</td>
@ -874,7 +882,22 @@ var options = {
Toggle if the nodes can be dragged. This will not affect the dragging of the network.
</td>
</tr>
<tr>
<td>hideNodesOnDrag</td>
<td>Boolean</td>
<td>false</td>
<td>
Toggle if the nodes are drawn during a drag. This can greatly improve performance if you have many nodes.
</td>
</tr>
<tr>
<td>hideEdgesOnDrag</td>
<td>Boolean</td>
<td>false</td>
<td>
Toggle if the edges are drawn during a drag. This can greatly improve performance if you have many edges.
</td>
</tr>
<tr>
<td><a href="#Navigation_controls">navigation</a></td>
<td>Object</td>
@ -895,11 +918,31 @@ var options = {
<tr>
<td>smoothCurves</td>
<td>Boolean || object</td>
<td>object</td>
<td>If true, edges are drawn as smooth curves. This is more computationally intensive since the edge now is a quadratic Bezier curve. This can be further configured by the options below.</td>
</tr>
<tr>
<td>smoothCurves.dynamic</td>
<td>Boolean</td>
<td>true</td>
<td>If true, edges are drawn as smooth curves. This is more computationally intensive since the edge now is a quadratic Bezier curve with control points on both nodes and an invisible node in the center of the edge. This support node is also handed by the physics simulation.</td>
<td>By default, the edges are dynamic. This means there are support nodes placed in the middle of the edge. This support node is also handed by the physics simulation. If false, the smoothness will be based on the
relative positions of the to and from nodes. This is computationally cheaper but there is no self organisation.</td>
</tr>
<tr>
<td>smoothCurves.adaptive</td>
<td>Boolean</td>
<td>true</td>
<td>This option only affects NONdynamic smooth curves. Adaptivity allows the smoothness to transition nicely if there is node movement. The downside is that the maximum roundness is less if adaptivity is enabled. Adaptivity can be disabled for static network.
Sudden switching between orientations can happen on an animated network.</td>
</tr>
<tr>
<td>smoothCurves.roundness</td>
<td>Number</td>
<td>0.5</td>
<td>This only affects NONdynamic smooth curves. The roundness can be tweaked with the parameter. The value range is from 0 to 1 with a maximum roundness at 0.5.</td>
</tr>
<tr>
<td>selectable</td>
<td>Boolean</td>
@ -1223,6 +1266,14 @@ var options = {
<td>The resting length of the edge when modeled as a spring. By default the springLength determined by the physics is used. By using this setting you can make certain edges have different resting lengths.</td>
</tr>
<tr>
<td>inheritColor</td>
<td>String | Boolean</td>
<td>false</td>
<td>Possible values: <code>"to","from", true, false</code>. If this value is set to false, the edge color information is used. If the value is set to true or "from",
the color data from the borders of the "from" node is used. If this value is "to", the color data from the borders of the "to" node is used.</td>
</tr>
<tr>
<td>style</td>
<td>String</td>

+ 5
- 5
examples/network/02_random_nodes.html View File

@ -81,12 +81,12 @@
},
stabilize: false
};
network = new vis.Network(container, data, options);
network = new vis.Network(container, data, options);
// add event listeners
network.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
// add event listeners
network.on('select', function(params) {
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
}
</script>
</head>

+ 1
- 0
examples/network/23_hierarchical_layout.html View File

@ -82,6 +82,7 @@
edges: {
},
stabilize: false,
smoothCurves: false,
hierarchicalLayout: {
direction: directionInput.value
}

+ 34
- 1
examples/network/24_hierarchical_layout_userdefined.html View File

@ -20,6 +20,7 @@
var nodes = null;
var edges = null;
var network = null;
var directionInput = document.getElementById("direction");
function draw() {
nodes = [];
@ -113,7 +114,9 @@
};
var options = {
hierarchicalLayout:true
hierarchicalLayout: {
direction: directionInput.value
}
};
network = new vis.Network(container, data, options);
@ -122,6 +125,7 @@
document.getElementById('selection').innerHTML = 'Selection: ' + params.nodes;
});
}
</script>
</head>
@ -129,11 +133,40 @@
<h2>Hierarchical Layout - User-defined</h2>
<div style="width:700px; font-size:14px;">
This example shows a user-defined hierarchical layout. If the user defines levels for nodes but does not do so for all nodes, an alert will show up and hierarchical layout will be disabled. Either all or none can be defined.
If the smooth curves appear to be inverted, the direction of the edge is not in the same direction as the network.
</div>
<input type="button" id="btn-UD" value="Up-Down">
<input type="button" id="btn-DU" value="Down-Up">
<input type="button" id="btn-LR" value="Left-Right">
<input type="button" id="btn-RL" value="Right-Left">
<input type="hidden" id='direction' value="UD">
<br />
<div id="mynetwork"></div>
<p id="selection"></p>
<script language="JavaScript">
var directionInput = document.getElementById("direction");
var btnUD = document.getElementById("btn-UD");
btnUD.onclick = function() {
directionInput.value = "UD";
draw();
}
var btnDU = document.getElementById("btn-DU");
btnDU.onclick = function() {
directionInput.value = "DU";
draw();
};
var btnLR = document.getElementById("btn-LR");
btnLR.onclick = function() {
directionInput.value = "LR";
draw();
};
var btnRL = document.getElementById("btn-RL");
btnRL.onclick = function() {
directionInput.value = "RL";
draw();
};
</script>
</body>
</html>

+ 75
- 0
examples/network/26_staticSmoothCurves.html View File

@ -0,0 +1,75 @@
<!doctype html>
<html>
<head>
<title>Network | Static smooth curves</title>
<script type="text/javascript" src="../../dist/vis.js"></script>
<style type="text/css">
#mynetwork {
width: 400px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<h2>Static smooth curves</h2>
<div style="width:700px; font-size:14px;">
All the smooth curves in the examples so far have been using dynamic smooth curves. This means that each curve has a
support node which takes part in the physics simulation. For large networks or dense clusters, this may not be the ideal
solution. To solve this, static smooth curves have been added. The static smooth curves are based only on the positions of the connected
nodes. There are multiple ways to determine the way this curve is drawn. This example shows the effect of the different
types. <br /> <br />
Drag the nodes around each other to see how the smooth curves are drawn for each setting. For animated system, we
recommend only the continuous mode. In the next example you can see the effect of these methods on a large network. Keep in mind
that the direction (the from and to) of the curve matters.
<br /> <br />
</div>
Smooth curve type:
<select id="dropdownID">
<option value="continuous">continuous</option>
<option value="discrete">discrete</option>
<option value="diagonalCross">diagonalCross</option>
<option value="straightCross">straightCross</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
</select>
<div id="mynetwork"></div>
<script type="text/javascript">
var dropdown = document.getElementById("dropdownID");
dropdown.onchange = update;
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
{id: 2, label: 'Node 2', x:150, y:130, allowedToMoveX: true, allowedToMoveY: true}
];
// create an array with edges
var edges = [
{from: 1, to: 2}
];
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {physics:{barnesHut:{gravitationalConstant:0, springConstant:0, centralGravity: 0}},
smoothCurves:{dynamic:false, type: '1'}};
var network = new vis.Network(container, data, options);
function update() {
var type = dropdown.value;
network.setOptions({smoothCurves:{type:type}});
}
</script>
</body>
</html>

+ 10089
- 0
examples/network/27_world_cup_network.html
File diff suppressed because it is too large
View File


+ 258
- 50
lib/network/Edge.js View File

@ -41,8 +41,10 @@ function Edge (properties, network, constants) {
this.customLength = false;
this.selected = false;
this.hover = false;
this.smooth = constants.smoothCurves;
this.smoothCurves = constants.smoothCurves;
this.dynamicSmoothCurves = constants.dynamicSmoothCurves;
this.arrowScaleFactor = constants.edges.arrowScaleFactor;
this.inheritColor = constants.edges.inheritColor;
this.from = null; // a node
this.to = null; // a node
@ -114,6 +116,8 @@ Edge.prototype.setProperties = function(properties, constants) {
// scale the arrow
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;}
if (properties.inheritColor !== undefined) {this.inheritColor = properties.inheritColor;}
// Added to support dashed lines
// David Jordan
// 2012-08-08
@ -258,6 +262,28 @@ Edge.prototype.isOverlappingWith = function(obj) {
}
};
Edge.prototype._getColor = function() {
var colorObj = this.color;
if (this.inheritColor == "to") {
colorObj = {
highlight: this.to.color.highlight.border,
hover: this.to.color.hover.border,
color: this.to.color.border
};
}
else if (this.inheritColor == "from" || this.inheritColor == true) {
colorObj = {
highlight: this.from.color.highlight.border,
hover: this.from.color.hover.border,
color: this.from.color.border
};
}
if (this.selected == true) {return colorObj.highlight;}
else if (this.hover == true) {return colorObj.hover;}
else {return colorObj.color;}
}
/**
* Redraw a edge as a line
@ -268,21 +294,19 @@ Edge.prototype.isOverlappingWith = function(obj) {
*/
Edge.prototype._drawLine = function(ctx) {
// set style
if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
else if (this.hover == true) {ctx.strokeStyle = this.color.hover;}
else {ctx.strokeStyle = this.color.color;}
ctx.lineWidth = this._getLineWidth();
ctx.strokeStyle = this._getColor();
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
// draw line
this._line(ctx);
var via = this._line(ctx);
// draw label
var point;
if (this.label) {
if (this.smooth == true) {
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
if (this.smoothCurves.enabled == true && via != null) {
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x));
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y));
point = {x:midpointX, y:midpointY};
}
else {
@ -332,6 +356,154 @@ Edge.prototype._getLineWidth = function() {
}
};
Edge.prototype._getViaCoordinates = function () {
var xVia = null;
var yVia = null;
var factor = this.smoothCurves.roundness;
var type = this.smoothCurves.type;
if (factor == 0) {
return {x:null,y:null};
}
var dx = Math.abs(this.from.x - this.to.x);
var dy = Math.abs(this.from.y - this.to.y);
if (type == 'discrete' || type == 'diagonalCross') {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
}
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
}
}
if (type == "discrete") {
xVia = dx < factor * dy ? this.from.x : xVia;
}
}
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
}
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
}
else if (this.from.x > this.to.x) {
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
}
}
if (type == "discrete") {
yVia = dy < factor * dx ? this.from.y : yVia;
}
}
}
else if (type == "straightCross") {
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
xVia = this.from.x;
yVia = this.to.y
}
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
xVia = this.to.x;
yVia = this.from.y;
}
}
else if (type == 'horizontal') {
xVia = this.to.x;
yVia = this.from.y;
}
else if (type == 'vertical') {
xVia = this.from.x;
yVia = this.to.y;
}
else { // continuous
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(1)
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(2)
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x > xVia ? this.to.x :xVia;
}
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(3)
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(4, this.from.x, this.to.x)
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
}
}
}
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(5)
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(6)
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(7)
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(8)
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
}
}
}
return {x:xVia, y:yVia};
}
/**
* Draw a line between two nodes
* @param {CanvasRenderingContext2D} ctx
@ -341,13 +513,33 @@ Edge.prototype._line = function (ctx) {
// draw a straight line
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
if (this.smooth == true) {
if (this.smoothCurves.enabled == true) {
if (this.smoothCurves.dynamic == false) {
var via = this._getViaCoordinates();
if (via.x == null) {
ctx.lineTo(this.to.x, this.to.y);
ctx.stroke();
return null;
}
else {
// this.via.x = via.x;
// this.via.y = via.y;
ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y);
ctx.stroke();
return via;
}
}
else {
ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y);
ctx.stroke();
return this.via;
}
}
else {
ctx.lineTo(this.to.x, this.to.y);
ctx.stroke();
return null;
}
ctx.stroke();
};
/**
@ -411,11 +603,9 @@ Edge.prototype._drawDashLine = function(ctx) {
ctx.lineWidth = this._getLineWidth();
var via = null;
// only firefox and chrome support this method, else we use the legacy one.
if (ctx.mozDash !== undefined || ctx.setLineDash !== undefined) {
ctx.beginPath();
ctx.moveTo(this.from.x, this.from.y);
// configure the dash pattern
var pattern = [0];
if (this.dash.length !== undefined && this.dash.gap !== undefined) {
@ -436,13 +626,7 @@ Edge.prototype._drawDashLine = function(ctx) {
}
// draw the line
if (this.smooth == true) {
ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y);
}
else {
ctx.lineTo(this.to.x, this.to.y);
}
ctx.stroke();
via = this._line(ctx);
// restore the dash settings.
if (typeof ctx.setLineDash !== 'undefined') { //Chrome
@ -479,9 +663,9 @@ Edge.prototype._drawDashLine = function(ctx) {
// draw label
if (this.label) {
var point;
if (this.smooth == true) {
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
if (this.smoothCurves.enabled == true && via != null) {
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x));
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y));
point = {x:midpointX, y:midpointY};
}
else {
@ -538,14 +722,14 @@ Edge.prototype._drawArrowCenter = function(ctx) {
if (this.from != this.to) {
// draw line
this._line(ctx);
var via = this._line(ctx);
var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
var length = (10 + 5 * this.width) * this.arrowScaleFactor;
// draw an arrow halfway the line
if (this.smooth == true) {
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
if (this.smoothCurves.enabled == true && via != null) {
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x));
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y));
point = {x:midpointX, y:midpointY};
}
else {
@ -625,20 +809,27 @@ Edge.prototype._drawArrow = function(ctx) {
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
var via;
if (this.smoothCurves.dynamic == true && this.smoothCurves.enabled == true ) {
via = this.via;
}
else if (this.smoothCurves.enabled == true) {
via = this._getViaCoordinates();
}
if (this.smooth == true) {
angle = Math.atan2((this.to.y - this.via.y), (this.to.x - this.via.x));
dx = (this.to.x - this.via.x);
dy = (this.to.y - this.via.y);
if (this.smoothCurves.enabled == true && via.x != null) {
angle = Math.atan2((this.to.y - via.y), (this.to.x - via.x));
dx = (this.to.x - via.x);
dy = (this.to.y - via.y);
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
}
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
var xTo,yTo;
if (this.smooth == true) {
xTo = (1 - toBorderPoint) * this.via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * this.via.y + toBorderPoint * this.to.y;
if (this.smoothCurves.enabled == true && via.x != null) {
xTo = (1 - toBorderPoint) * via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * via.y + toBorderPoint * this.to.y;
}
else {
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
@ -647,8 +838,8 @@ Edge.prototype._drawArrow = function(ctx) {
ctx.beginPath();
ctx.moveTo(xFrom,yFrom);
if (this.smooth == true) {
ctx.quadraticCurveTo(this.via.x,this.via.y,xTo, yTo);
if (this.smoothCurves.enabled == true && via.x != null) {
ctx.quadraticCurveTo(via.x,via.y,xTo, yTo);
}
else {
ctx.lineTo(xTo, yTo);
@ -664,9 +855,9 @@ Edge.prototype._drawArrow = function(ctx) {
// draw label
if (this.label) {
var point;
if (this.smooth == true) {
var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
var midpointY = 0.5*(0.5*(this.from.y + this.via.y) + 0.5*(this.to.y + this.via.y));
if (this.smoothCurves.enabled == true && via != null) {
var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x));
var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y));
point = {x:midpointX, y:midpointY};
}
else {
@ -736,13 +927,23 @@ Edge.prototype._drawArrow = function(ctx) {
*/
Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
if (this.from != this.to) {
if (this.smooth == true) {
if (this.smoothCurves.enabled == true) {
var xVia, yVia;
if (this.smoothCurves.enabled == true && this.smoothCurves.dynamic == true) {
xVia = this.via.x;
yVia = this.via.y;
}
else {
var via = this._getViaCoordinates();
xVia = via.x;
yVia = via.y;
}
var minDistance = 1e9;
var i,t,x,y,dx,dy;
for (i = 0; i < 10; i++) {
t = 0.1*i;
x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*this.via.x + Math.pow(t,2)*x2;
y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*this.via.y + Math.pow(t,2)*y2;
x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2;
y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2;
dx = Math.abs(x3-x);
dy = Math.abs(y3-y);
minDistance = Math.min(minDistance,Math.sqrt(dx*dx + dy*dy));
@ -943,20 +1144,27 @@ Edge.prototype.getControlNodePositions = function(ctx) {
var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
var via;
if (this.smoothCurves.dynamic == true && this.smoothCurves.enabled == true) {
via = this.via;
}
else if (this.smoothCurves.enabled == true) {
via = this._getViaCoordinates();
}
if (this.smooth == true) {
angle = Math.atan2((this.to.y - this.via.y), (this.to.x - this.via.x));
dx = (this.to.x - this.via.x);
dy = (this.to.y - this.via.y);
if (this.smoothCurves.enabled == true && via.x != null) {
angle = Math.atan2((this.to.y - via.y), (this.to.x - via.x));
dx = (this.to.x - via.x);
dy = (this.to.y - via.y);
edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
}
var toBorderDist = this.to.distanceToBorder(ctx, angle);
var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
var xTo,yTo;
if (this.smooth == true) {
xTo = (1 - toBorderPoint) * this.via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * this.via.y + toBorderPoint * this.to.y;
if (this.smoothCurves.enabled == true && via.x != null) {
xTo = (1 - toBorderPoint) * via.x + toBorderPoint * this.to.x;
yTo = (1 - toBorderPoint) * via.y + toBorderPoint * this.to.y;
}
else {
xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;

+ 10
- 10
lib/network/Groups.js View File

@ -14,16 +14,16 @@ function Groups() {
* default constants for group colors
*/
Groups.DEFAULT = [
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}}, // red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}}, // green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}}, // orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}} // mint
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint
];

+ 54
- 19
lib/network/Network.js View File

@ -56,9 +56,9 @@ function Network (container, data, options) {
// set constant values
this.constants = {
nodes: {
radiusMin: 5,
radiusMax: 20,
radius: 5,
radiusMin: 10,
radiusMax: 30,
radius: 10,
shape: 'ellipse',
image: undefined,
widthMin: 16, // px
@ -106,7 +106,8 @@ function Network (container, data, options) {
length: 10,
gap: 5,
altLength: undefined
}
},
inheritColor: false // to, from, false, true (== from)
},
configurePhysics:false,
physics: {
@ -178,7 +179,13 @@ function Network (container, data, options) {
direction: "UD" // UD, DU, LR, RL
},
freezeForStabilization: false,
smoothCurves: true,
smoothCurves: {
enabled: true,
dynamic: true,
type: "continuous",
roundness: 0.5
},
dynamicSmoothCurves: true,
maxVelocity: 10,
minVelocity: 0.1, // px/s
stabilizationIterations: 1000, // maximum number of iteration to stabilize
@ -213,10 +220,12 @@ function Network (container, data, options) {
dragNetwork: true,
dragNodes: true,
zoomable: true,
hover: false
hover: false,
hideEdgesOnDrag: false,
hideNodesOnDrag: false
};
this.hoverObj = {nodes:{},edges:{}};
this.controlNodesActive = false;
// Node variables
var network = this;
@ -549,7 +558,6 @@ Network.prototype.setOptions = function (options) {
if (options.height !== undefined) {this.height = options.height;}
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
@ -557,6 +565,8 @@ Network.prototype.setOptions = function (options) {
if (options.dragNodes !== undefined) {this.constants.dragNodes = options.dragNodes;}
if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
if (options.hover !== undefined) {this.constants.hover = options.hover;}
if (options.hideEdgesOnDrag !== undefined) {this.constants.hideEdgesOnDrag = options.hideEdgesOnDrag;}
if (options.hideNodesOnDrag !== undefined) {this.constants.hideNodesOnDrag = options.hideNodesOnDrag;}
// TODO: deprecated since version 3.0.0. Cleanup some day
if (options.dragGraph !== undefined) {
@ -622,6 +632,20 @@ Network.prototype.setOptions = function (options) {
}
}
if (options.smoothCurves !== undefined) {
if (typeof options.smoothCurves == 'boolean') {
this.constants.smoothCurves.enabled = options.smoothCurves;
}
else {
this.constants.smoothCurves.enabled = true;
for (prop in options.smoothCurves) {
if (options.smoothCurves.hasOwnProperty(prop)) {
this.constants.smoothCurves[prop] = options.smoothCurves[prop];
}
}
}
}
if (options.hierarchicalLayout) {
this.constants.hierarchicalLayout.enabled = true;
for (prop in options.hierarchicalLayout) {
@ -693,7 +717,6 @@ Network.prototype.setOptions = function (options) {
}
}
if (options.edges.color !== undefined) {
if (util.isString(options.edges.color)) {
this.constants.edges.color = {};
@ -1023,10 +1046,11 @@ Network.prototype._handleOnDrag = function(event) {
this._setTranslation(
this.drag.translation.x + diffX,
this.drag.translation.y + diffY);
this.drag.translation.y + diffY
);
this._redraw();
this.moving = true;
this.start();
// this.moving = true;
// this.start();
}
}
};
@ -1045,6 +1069,7 @@ Network.prototype._onDragEnd = function () {
s.node.yFixed = s.yFixed;
});
}
this._redraw();
};
/**
@ -1740,10 +1765,19 @@ Network.prototype._redraw = function() {
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
};
this._doInAllSectors("_drawAllSectorNodes",ctx);
this._doInAllSectors("_drawEdges",ctx);
this._doInAllSectors("_drawNodes",ctx,false);
this._doInAllSectors("_drawControlNodes",ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges",ctx);
}
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) {
this._doInAllSectors("_drawNodes",ctx,false);
}
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes",ctx);
}
// this._doInSupportSector("_drawNodes",ctx,true);
// this._drawTree(ctx,"#F00F0F");
@ -2055,7 +2089,7 @@ Network.prototype._discreteStepNodes = function() {
this.moving = true;
}
else {
this.moving = this._isMoving(vminCorrected);
this.moving = this._isMoving(vminCorrected) || this.constants.configurePhysics;
}
}
};
@ -2104,10 +2138,12 @@ Network.prototype._animationStep = function() {
timeRequired = Date.now() - calculationTime;
maxSteps++;
}
// start the rendering process
var renderTime = Date.now();
this._redraw();
this.renderTime = Date.now() - renderTime;
};
if (typeof window !== 'undefined') {
@ -2191,8 +2227,7 @@ Network.prototype._configureSmoothCurves = function(disableStart) {
if (disableStart === undefined) {
disableStart = true;
}
if (this.constants.smoothCurves == true) {
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
this._createBezierNodes();
}
else {
@ -2220,7 +2255,7 @@ Network.prototype._configureSmoothCurves = function(disableStart) {
* @private
*/
Network.prototype._createBezierNodes = function() {
if (this.constants.smoothCurves == true) {
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
for (var edgeId in this.edges) {
if (this.edges.hasOwnProperty(edgeId)) {
var edge = this.edges[edgeId];

+ 14
- 1
lib/network/mixins/HierarchicalLayoutMixin.js View File

@ -23,6 +23,17 @@ exports._setupHierarchicalLayout = function() {
else {
this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation);
}
if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "LR") {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "vertical";
}
}
else {
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.type = "horizontal";
}
}
// get the size of the largest hubs and check if the user has defined a level for a node.
var hubsize = 0;
var node, nodeId;
@ -210,7 +221,9 @@ exports._changeConstants = function() {
this.constants.physics.barnesHut.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabled = true;
this._loadSelectedForceSolver();
this.constants.smoothCurves = false;
if (this.constants.smoothCurves.enabled == true) {
this.constants.smoothCurves.dynamic = false;
}
this._configureSmoothCurves();
};

+ 3
- 0
lib/network/mixins/ManipulationMixin.js View File

@ -63,10 +63,12 @@ exports._createManipulatorBar = function() {
if (this.boundFunction) {
this.off('select', this.boundFunction);
}
if (this.edgeBeingEdited !== undefined) {
this.edgeBeingEdited._disableControlNodes();
this.edgeBeingEdited = undefined;
this.selectedControlNode = null;
this.controlNodesActive = false;
}
// restore overloaded functions
@ -226,6 +228,7 @@ exports._createAddEdgeToolbar = function() {
exports._createEditEdgeToolbar = function() {
// clear the toolbar
this._clearManipulatorBar();
this.controlNodesActive = true;
if (this.boundFunction) {
this.off('select', this.boundFunction);

+ 17
- 14
lib/network/mixins/physics/PhysicsMixin.js View File

@ -95,15 +95,17 @@ exports._calculateForces = function () {
this._calculateGravitationalForces();
this._calculateNodeForces();
if (this.constants.smoothCurves == true) {
this._calculateSpringForcesWithSupport();
}
else {
if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._calculateHierarchicalSpringForces();
if (this.constants.springConstant > 0) {
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
this._calculateSpringForcesWithSupport();
}
else {
this._calculateSpringForces();
if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
this._calculateHierarchicalSpringForces();
}
else {
this._calculateSpringForces();
}
}
}
};
@ -118,7 +120,7 @@ exports._calculateForces = function () {
* @private
*/
exports._updateCalculationNodes = function () {
if (this.constants.smoothCurves == true) {
if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) {
this.calculationNodes = {};
this.calculationNodeIndices = [];
@ -457,7 +459,7 @@ exports._loadPhysicsConfiguration = function () {
graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this);
graph_repositionNodes.onclick = graphRepositionNodes.bind(this);
graph_generateOptions.onclick = graphGenerateOptions.bind(this);
if (this.constants.smoothCurves == true) {
if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) {
graph_toggleSmooth.style.background = "#A4FF56";
}
else {
@ -498,9 +500,9 @@ exports._overWriteGraphConstants = function (constantsVariableName, value) {
* this function is bound to the toggle smooth curves button. That is also why it is not in the prototype.
*/
function graphToggleSmoothCurves () {
this.constants.smoothCurves = !this.constants.smoothCurves;
this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled;
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";}
if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";}
else {graph_toggleSmooth.style.background = "#FF8532";}
this._configureSmoothCurves(false);
@ -552,10 +554,10 @@ function graphGenerateOptions () {
}
options += '}}'
}
if (this.constants.smoothCurves != this.backupConstants.smoothCurves) {
if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) {
if (optionsSpecific.length == 0) {options = "var options = {";}
else {options += ", "}
options += "smoothCurves: " + this.constants.smoothCurves;
options += "smoothCurves: " + this.constants.smoothCurves.enabled;
}
if (options != "No options are required, default values used.") {
options += '};'
@ -653,6 +655,7 @@ function switchConfigurations () {
this.constants.hierarchicalLayout.enabled = true;
this.constants.physics.hierarchicalRepulsion.enabled = true;
this.constants.physics.barnesHut.enabled = false;
this.constants.smoothCurves.enabled = false;
this._setupHierarchicalLayout();
}
}
@ -663,7 +666,7 @@ function switchConfigurations () {
}
this._loadSelectedForceSolver();
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";}
if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";}
else {graph_toggleSmooth.style.background = "#FF8532";}
this.moving = true;
this.start();

Loading…
Cancel
Save