Browse Source

put all nodes and edges into one container. keep track of indices for visible and physics activated nodes

flowchartTest
Alex de Mulder 9 years ago
parent
commit
ca11929e85
14 changed files with 19697 additions and 19630 deletions
  1. +19533
    -19499
      dist/vis.js
  2. +25
    -8
      lib/network/Network.js
  3. +36
    -30
      lib/network/modules/CanvasRenderer.js
  4. +3
    -1
      lib/network/modules/EdgesHandler.js
  5. +3
    -1
      lib/network/modules/NodesHandler.js
  6. +27
    -24
      lib/network/modules/PhysicsEngine.js
  7. +12
    -8
      lib/network/modules/components/edges/EdgeMain.js
  8. +1
    -1
      lib/network/modules/components/nodes/NodeMain.js
  9. +2
    -2
      lib/network/modules/components/physics/BarnesHutSolver.js
  10. +3
    -2
      lib/network/modules/components/physics/CentralGravitySolver.js
  11. +2
    -2
      lib/network/modules/components/physics/HierarchicalRepulsionSolver.js
  12. +31
    -32
      lib/network/modules/components/physics/HierarchicalSpringSolver.js
  13. +2
    -2
      lib/network/modules/components/physics/RepulsionSolver.js
  14. +17
    -18
      lib/network/modules/components/physics/SpringSolver.js

+ 19533
- 19499
dist/vis.js
File diff suppressed because it is too large
View File


+ 25
- 8
lib/network/Network.js View File

@ -63,9 +63,8 @@ function Network (container, data, options) {
this.body = {
nodes: {},
nodeIndices: [],
supportNodes: {},
supportNodeIndices: [],
edges: {},
edgeIndices: [],
data: {
nodes: null, // A DataSet or DataView
edges: null // A DataSet or DataView
@ -111,7 +110,7 @@ function Network (container, data, options) {
this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms
this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into
this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations
this.layoutEngine = new LayoutEngine(this.body); // TODO: layout engine for initial positioning and hierarchical positioning
this.layoutEngine = new LayoutEngine(this.body);
this.clustering = new ClusterEngine(this.body); // clustering api
this.nodesHandler = new NodesHandler(this.body, images, groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options
@ -123,8 +122,8 @@ function Network (container, data, options) {
this.body.emitter.on("_dataChanged", (params) => {
var t0 = new Date().valueOf();
// update shortcut lists
this._updateNodeIndexList();
this.physics._updateCalculationNodes();
this._updateVisibleIndices();
this.physics._updatePhysicsIndices();
// update values
this._updateValueRange(this.body.nodes);
this._updateValueRange(this.body.edges);
@ -169,9 +168,27 @@ Emitter(Network.prototype);
* Update the this.body.nodeIndices with the most recent node index list
* @private
*/
Network.prototype._updateNodeIndexList = function() {
this.body.supportNodeIndices = Object.keys(this.body.supportNodes)
this.body.nodeIndices = Object.keys(this.body.nodes);
Network.prototype._updateVisibleIndices = function() {
let nodes = this.body.nodes;
let edges = this.body.edges;
this.body.nodeIndices = [];
this.body.edgeIndices = [];
for (let nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
if (nodes[nodeId].options.hidden === false) {
this.body.nodeIndices.push(nodeId);
}
}
}
for (let edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
if (edges[edgeId].options.hidden === false) {
this.body.edgeIndices.push(edgeId);
}
}
}
};

+ 36
- 30
lib/network/modules/CanvasRenderer.js View File

@ -148,14 +148,13 @@ class CanvasRenderer {
}
if (this.dragging === false || (this.dragging === true && this.options.hideNodesOnDrag === false)) {
this._drawNodes(ctx, this.body.nodes, hidden);
this._drawNodes(ctx, hidden);
}
if (this.controlNodesActive === true) {
this._drawControlNodes(ctx);
}
//this._drawNodes(ctx,this.body.supportNodes,true);
//this.physics.nodesSolver._debug(ctx,"#F00F0F");
// restore original scaling and translation
@ -176,31 +175,35 @@ class CanvasRenderer {
* @param {Boolean} [alwaysShow]
* @private
*/
_drawNodes(ctx,nodes,alwaysShow = false) {
// first draw the unselected nodes
_drawNodes(ctx,alwaysShow = false) {
var nodes = this.body.nodes;
var nodeIndices = this.body.nodeIndices;
var node;
var selected = [];
for (var id in nodes) {
if (nodes.hasOwnProperty(id)) {
nodes[id].setScaleAndPos(this.body.view.scale,this.canvasTopLeft,this.canvasBottomRight);
if (nodes[id].isSelected()) {
selected.push(id);
// draw unselected nodes;
for (let i = 0; i < nodeIndices.length; i++) {
node = nodes[nodeIndices[i]];
node.setScaleAndPos(this.body.view.scale,this.canvasTopLeft,this.canvasBottomRight);
// set selected nodes aside
if (node.isSelected()) {
selected.push(nodeIndices[i]);
}
else {
if (alwaysShow === true) {
node.draw(ctx);
}
else {
if (alwaysShow === true) {
nodes[id].draw(ctx);
}
else if (nodes[id].inArea() === true) {
nodes[id].draw(ctx);
}
else if (node.inArea() === true) {
node.draw(ctx);
}
}
}
// draw the selected nodes on top
for (var s = 0, sMax = selected.length; s < sMax; s++) {
if (nodes[selected[s]].inArea() || alwaysShow) {
nodes[selected[s]].draw(ctx);
for (let i = 0; i < selected.length; i++) {
node = nodes[selected[i]];
if (node.inArea() || alwaysShow) {
node.draw(ctx);
}
}
}
@ -214,13 +217,14 @@ class CanvasRenderer {
*/
_drawEdges(ctx) {
var edges = this.body.edges;
for (var id in edges) {
if (edges.hasOwnProperty(id)) {
var edge = edges[id];
edge.setScale(this.body.view.scale);
if (edge.connected === true) {
edges[id].draw(ctx);
}
var edgeIndices = this.body.edgeIndices;
var edge;
for (let i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
edge.setScale(this.body.view.scale);
if (edge.connected === true) {
edge.draw(ctx);
}
}
}
@ -233,10 +237,12 @@ class CanvasRenderer {
*/
_drawControlNodes(ctx) {
var edges = this.body.edges;
for (var id in edges) {
if (edges.hasOwnProperty(id)) {
edges[id]._drawControlNodes(ctx);
}
var edgeIndices = this.body.edgeIndices;
var edge;
for (let i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
edge._drawControlNodes(ctx);
}
}

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

@ -67,7 +67,9 @@ class EdgesHandler {
dynamic: true,
type: "continuous",
roundness: 0.5
}
},
hidden: false,
physics: true
};
util.extend(this.options, this.defaultOptions);

+ 3
- 1
lib/network/modules/NodesHandler.js View File

@ -71,7 +71,9 @@ class NodesHandler {
},
group: undefined,
borderWidth: 1,
borderWidthSelected: undefined
borderWidthSelected: undefined,
physics: true,
hidden: false
};
util.extend(this.options, this.defaultOptions);

+ 27
- 24
lib/network/modules/PhysicsEngine.js View File

@ -17,7 +17,7 @@ var util = require('../../util');
class PhysicsEngine {
constructor(body) {
this.body = body;
this.physicsBody = {calculationNodes: {}, calculationNodeIndices:[], forces: {}, velocities: {}};
this.physicsBody = {physicsNodeIndices:[], physicsEdgeIndices:[], forces: {}, velocities: {}};
this.simulationInterval = 1000 / 60;
this.requiresTimeout = true;
@ -213,31 +213,34 @@ class PhysicsEngine {
*
* @private
*/
_updateCalculationNodes() {
this.physicsBody.calculationNodes = {};
_updatePhysicsIndices() {
this.physicsBody.forces = {};
this.physicsBody.calculationNodeIndices = [];
for (let i = 0; i < this.body.nodeIndices.length; i++) {
let nodeId = this.body.nodeIndices[i];
this.physicsBody.calculationNodes[nodeId] = this.body.nodes[nodeId];
this.physicsBody.physicsNodeIndices = [];
this.physicsBody.physicsEdgeIndices = [];
let nodes = this.body.nodes;
let edges = this.body.edges;
// get node indices for physics
for (let nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
if (nodes[nodeId].options.physics === true) {
this.physicsBody.physicsNodeIndices.push(nodeId);
}
}
}
// if support nodes are used, we have them here
var supportNodes = this.body.supportNodes;
for (let i = 0; i < this.body.supportNodeIndices.length; i++) {
let supportNodeId = this.body.supportNodeIndices[i];
if (this.body.edges[supportNodes[supportNodeId].parentEdgeId] !== undefined) {
this.physicsBody.calculationNodes[supportNodeId] = supportNodes[supportNodeId];
}
else {
console.error("Support node detected that does not have an edge!")
// get edge indices for physics
for (let edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
if (edges[edgeId].options.physics === true) {
this.physicsBody.physicsEdgeIndices.push(edgeId);
}
}
}
this.physicsBody.calculationNodeIndices = Object.keys(this.physicsBody.calculationNodes);
for (let i = 0; i < this.physicsBody.calculationNodeIndices.length; i++) {
let nodeId = this.physicsBody.calculationNodeIndices[i];
// get the velocity and the forces vector
for (let i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) {
let nodeId = this.physicsBody.physicsNodeIndices[i];
this.physicsBody.forces[nodeId] = {x:0,y:0};
// forces can be reset because they are recalculated. Velocities have to persist.
@ -248,7 +251,7 @@ class PhysicsEngine {
// clean deleted nodes from the velocity vector
for (let nodeId in this.physicsBody.velocities) {
if (this.physicsBody.calculationNodes[nodeId] === undefined) {
if (nodes[nodeId] === undefined) {
delete this.physicsBody.velocities[nodeId];
}
}
@ -257,7 +260,7 @@ class PhysicsEngine {
revert() {
var nodeIds = Object.keys(this.previousStates);
var nodes = this.physicsBody.calculationNodes;
var nodes = this.body.nodes;
var velocities = this.physicsBody.velocities;
for (let i = 0; i < nodeIds.length; i++) {
@ -276,7 +279,7 @@ class PhysicsEngine {
moveNodes() {
var nodesPresent = false;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var maxVelocity = this.options.maxVelocity === 0 ? 1e9 : this.options.maxVelocity;
var stabilized = true;
var vminCorrected = this.options.minVelocity / Math.max(this.body.view.scale,0.05);
@ -302,7 +305,7 @@ class PhysicsEngine {
}
_performStep(nodeId,maxVelocity) {
var node = this.physicsBody.calculationNodes[nodeId];
var node = this.body.nodes[nodeId];
var timestep = this.options.timestep;
var forces = this.physicsBody.forces;
var velocities = this.physicsBody.velocities;

+ 12
- 8
lib/network/modules/components/edges/EdgeMain.js View File

@ -79,7 +79,7 @@ class Edge {
var fields = ['style', 'fontSize', 'fontFace', 'fontColor', 'fontFill', 'fontStrokeWidth', 'fontStrokeColor', 'width',
'widthSelectionMultiplier', 'hoverWidth', 'arrowScaleFactor', 'dash', 'inheritColor', 'labelAlignment', 'opacity',
'customScalingFunction', 'useGradients', 'value','smooth'
'customScalingFunction', 'useGradients', 'value','smooth','hidden','physics'
];
util.selectiveDeepExtend(fields, this.options, options);
@ -137,7 +137,7 @@ class Edge {
this.widthSelected = this.options.width * this.options.widthSelectionMultiplier;
this.setupsmooth(this.initializing === false);
this.setupSmoothEdges(this.initializing === false);
// set draw method based on style
switch (this.options.style) {
@ -166,7 +166,7 @@ class Edge {
*
* @private
*/
setupsmooth(emitChange = true) {
setupSmoothEdges(emitChange = true) {
var changedData = false;
if (this.options.smooth.enabled == true && this.options.smooth.dynamic == true) {
if (this.via === undefined) {
@ -176,9 +176,11 @@ class Edge {
id: nodeId,
mass: 1,
shape: 'circle',
image: ""
image: "",
physics:true,
hidden:true
});
this.body.supportNodes[nodeId] = node;
this.body.nodes[nodeId] = node;
this.via = node;
this.via.parentEdgeId = this.id;
this.positionBezierNode();
@ -186,7 +188,7 @@ class Edge {
}
else {
if (this.via !== undefined) {
delete this.body.supportNodes[this.via.id];
delete this.body.nodes[this.via.id];
this.via = undefined;
changedData = true;
}
@ -1352,7 +1354,9 @@ class Edge {
color: {background: '#ff0000', border: '#3c3c3c', highlight: {background: '#07f968'}},
radius: 7,
borderWidth: 2,
borderWidthSelected: 2
borderWidthSelected: 2,
hidden: false,
physics: false
};
var nodeToOptions = util.deepExtend({},nodeFromOptions);
nodeToOptions.id = nodeIdTo;
@ -1497,7 +1501,7 @@ class Edge {
*/
getControlNodeToPosition(ctx) {
// draw arrow head
var controlnodeFromPos, controlnodeToPos;
var controlnodeToPos;
if (this.options.smooth.enabled == true) {
controlnodeToPos = this._findBorderPosition(false, ctx);
}

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

@ -108,7 +108,7 @@ Node.prototype.setOptions = function(properties, constants) {
var fields = ['borderWidth', 'borderWidthSelected', 'shape', 'image', 'brokenImage', 'radius', 'fontColor',
'fontSize', 'fontFace', 'fontFill', 'fontStrokeWidth', 'fontStrokeColor', 'group', 'mass', 'fontDrawThreshold',
'scaleFontWithValue', 'fontSizeMaxVisible', 'customScalingFunction', 'iconFontFace', 'icon', 'iconColor', 'iconSize',
'value'
'value','hidden','physics'
];
util.selectiveDeepExtend(fields, this.options, properties);

+ 2
- 2
lib/network/modules/components/physics/BarnesHutSolver.js View File

@ -24,8 +24,8 @@ class BarnesHutSolver {
solve() {
if (this.options.gravitationalConstant != 0) {
var node;
var nodes = this.physicsBody.calculationNodes;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var nodeCount = nodeIndices.length;
// create the tree

+ 3
- 2
lib/network/modules/components/physics/CentralGravitySolver.js View File

@ -15,10 +15,11 @@ class CentralGravitySolver {
solve() {
var dx, dy, distance, node, i;
var nodes = this.physicsBody.calculationNodes;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
var gravity = this.options.centralGravity;
var gravityForce = 0;

+ 2
- 2
lib/network/modules/components/physics/HierarchicalRepulsionSolver.js View File

@ -22,8 +22,8 @@ class HierarchicalRepulsionSolver {
solve() {
var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j;
var nodes = this.physicsBody.calculationNodes;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// repulsing forces between nodes

+ 31
- 32
lib/network/modules/components/physics/HierarchicalSpringSolver.js View File

@ -19,11 +19,13 @@ class HierarchicalSpringSolver {
* @private
*/
solve() {
var edgeLength, edge, edgeId;
var edgeLength, edge;
var dx, dy, fx, fy, springForce, distance;
var edges = this.body.edges;
var factor = 0.5;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var edgeIndices = this.physicsBody.physicsEdgeIndices;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// initialize the spring force counters
@ -35,36 +37,33 @@ class HierarchicalSpringSolver {
// forces caused by the edges, modelled as springs
for (edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
edge = edges[edgeId];
if (edge.connected === true) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
dx = (edge.from.x - edge.to.x);
dy = (edge.from.y - edge.to.y);
distance = Math.sqrt(dx * dx + dy * dy);
distance = distance == 0 ? 0.01 : distance;
// the 1/distance is so the fx and fy can be calculated without sine or cosine.
springForce = this.options.springConstant * (edgeLength - distance) / distance;
fx = dx * springForce;
fy = dy * springForce;
if (edge.to.level != edge.from.level) {
forces[edge.toId].springFx -= fx;
forces[edge.toId].springFy -= fy;
forces[edge.fromId].springFx += fx;
forces[edge.fromId].springFy += fy;
}
else {
let factor = 0.5;
forces[edge.toId].x -= factor*fx;
forces[edge.toId].y -= factor*fy;
forces[edge.fromId].x += factor*fx;
forces[edge.fromId].y += factor*fy;
}
for (let i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
if (edge.connected === true) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
dx = (edge.from.x - edge.to.x);
dy = (edge.from.y - edge.to.y);
distance = Math.sqrt(dx * dx + dy * dy);
distance = distance == 0 ? 0.01 : distance;
// the 1/distance is so the fx and fy can be calculated without sine or cosine.
springForce = this.options.springConstant * (edgeLength - distance) / distance;
fx = dx * springForce;
fy = dy * springForce;
if (edge.to.level != edge.from.level) {
forces[edge.toId].springFx -= fx;
forces[edge.toId].springFy -= fy;
forces[edge.fromId].springFx += fx;
forces[edge.fromId].springFy += fy;
}
else {
forces[edge.toId].x -= factor*fx;
forces[edge.toId].y -= factor*fy;
forces[edge.fromId].x += factor*fx;
forces[edge.fromId].y += factor*fy;
}
}
}

+ 2
- 2
lib/network/modules/components/physics/RepulsionSolver.js View File

@ -21,8 +21,8 @@ class RepulsionSolver {
solve() {
var dx, dy, distance, fx, fy, repulsingForce, node1, node2;
var nodes = this.physicsBody.calculationNodes;
var nodeIndices = this.physicsBody.calculationNodeIndices;
var nodes = this.body.nodes;
var nodeIndices = this.physicsBody.physicsNodeIndices;
var forces = this.physicsBody.forces;
// repulsing forces between nodes

+ 17
- 18
lib/network/modules/components/physics/SpringSolver.js View File

@ -19,28 +19,27 @@ class SpringSolver {
* @private
*/
solve() {
var edgeLength, edge, edgeId;
var edgeLength, edge;
var edgeIndices = this.physicsBody.physicsEdgeIndices;
var edges = this.body.edges;
// forces caused by the edges, modelled as springs
for (edgeId in edges) {
if (edges.hasOwnProperty(edgeId)) {
edge = edges[edgeId];
if (edge.connected === true) {
// only calculate forces if nodes are in the same sector
if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
if (edge.via != null) {
var node1 = edge.to;
var node2 = edge.via;
var node3 = edge.from;
for (let i = 0; i < edgeIndices.length; i++) {
edge = edges[edgeIndices[i]];
if (edge.connected === true) {
// only calculate forces if nodes are in the same sector
if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
if (edge.via != null) {
var node1 = edge.to;
var node2 = edge.via;
var node3 = edge.from;
this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
}
else {
this._calculateSpringForce(edge.from, edge.to, edgeLength);
}
this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
}
else {
this._calculateSpringForce(edge.from, edge.to, edgeLength);
}
}
}

Loading…
Cancel
Save