Browse Source

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

flowchartTest
Alex de Mulder 10 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