Browse Source

- added improved timestep adaption, fixed bugs in stabilization

kamadaKawai
Alex de Mulder 9 years ago
parent
commit
36ecb2c933
3 changed files with 30 additions and 49 deletions
  1. +15
    -24
      dist/vis.js
  2. +1
    -2
      lib/network/modules/LayoutEngine.js
  3. +14
    -23
      lib/network/modules/PhysicsEngine.js

+ 15
- 24
dist/vis.js View File

@ -5,7 +5,7 @@
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 4.7.1-SNAPSHOT * @version 4.7.1-SNAPSHOT
* @date 2015-08-13
* @date 2015-08-14
* *
* @license * @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -33018,9 +33018,9 @@ return /******/ (function(modules) { // webpackBootstrap
this.requiresTimeout = true; this.requiresTimeout = true;
this.previousStates = {}; this.previousStates = {};
this.referenceState = {}; this.referenceState = {};
this.adaptive = false;
this.adaptiveTimestep = true;
this.adaptiveCounter = 0; this.adaptiveCounter = 0;
this.adaptiveInterval = 3;
this.adaptiveInterval = 2;
this.freezeCache = {}; this.freezeCache = {};
this.renderTimer = undefined; this.renderTimer = undefined;
@ -33067,7 +33067,7 @@ return /******/ (function(modules) { // webpackBootstrap
damping: 0.09 damping: 0.09
}, },
maxVelocity: 50, maxVelocity: 50,
minVelocity: 0.1, // px/s
minVelocity: 0.75, // px/s
solver: 'barnesHut', solver: 'barnesHut',
stabilization: { stabilization: {
enabled: true, enabled: true,
@ -33210,7 +33210,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.stabilized = false; this.stabilized = false;
// when visible, adaptivity is disabled. // when visible, adaptivity is disabled.
this.adaptive = false;
//this.adaptiveTimestep = false;
// this sets the width of all nodes initially which could be required for the avoidOverlap // this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes"); this.body.emitter.emit("_resizeNodes");
@ -33297,10 +33297,11 @@ return /******/ (function(modules) { // webpackBootstrap
value: function physicsTick() { value: function physicsTick() {
if (this.stabilized === false) { if (this.stabilized === false) {
// adaptivity means the timestep adapts to the situation, only applicable for stabilization // adaptivity means the timestep adapts to the situation, only applicable for stabilization
if (this.adaptive === true) {
if (this.adaptiveTimestep === true) {
this.adaptiveCounter += 1; this.adaptiveCounter += 1;
if (this.adaptiveCounter % this.adaptiveInterval === 0) { if (this.adaptiveCounter % this.adaptiveInterval === 0) {
// we leave the timestep stable for "interval" iterations. // we leave the timestep stable for "interval" iterations.
//console.log(this.timestep)
// first the big step and revert. Revert saves the reference state. // first the big step and revert. Revert saves the reference state.
this.timestep = 2 * this.timestep; this.timestep = 2 * this.timestep;
this.calculateForces(); this.calculateForces();
@ -33318,18 +33319,18 @@ return /******/ (function(modules) { // webpackBootstrap
// we compare the two steps. if it is acceptable we double the step. // we compare the two steps. if it is acceptable we double the step.
if (this.compare() === true) { if (this.compare() === true) {
this.timestep = 2 * this.timestep;
this.timestep = 1.1 * this.timestep;
} else { } else {
// if not, we half the step to a minimum of the options timestep. // if not, we half the step to a minimum of the options timestep.
// if the half the timestep is smaller than the options step, we do not reset the counter // if the half the timestep is smaller than the options step, we do not reset the counter
// we assume that the options timestep is stable enough. // we assume that the options timestep is stable enough.
if (0.5 * this.timestep < this.options.timestep) {
if (0.9 * this.timestep < this.options.timestep) {
this.timestep = this.options.timestep; this.timestep = this.options.timestep;
} else { } else {
// if the timestep was larger than 2 times the option one we check the adaptivity again to ensure // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure
// that large instabilities do not form. // that large instabilities do not form.
this.adaptiveCounter = -1; // check again next iteration this.adaptiveCounter = -1; // check again next iteration
this.timestep = 0.5 * this.timestep;
this.timestep = 0.9 * this.timestep;
} }
} }
} else { } else {
@ -33476,26 +33477,17 @@ return /******/ (function(modules) { // webpackBootstrap
var nodesPresent = false; var nodesPresent = false;
var nodeIndices = this.physicsBody.physicsNodeIndices; var nodeIndices = this.physicsBody.physicsNodeIndices;
var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9;
var stabilized = true;
var vminCorrected = this.options.minVelocity / Math.max(this.body.view.scale, 0.05);
var maxNodeVelocity = 0;
for (var i = 0; i < nodeIndices.length; i++) { for (var i = 0; i < nodeIndices.length; i++) {
var nodeId = nodeIndices[i]; var nodeId = nodeIndices[i];
var nodeVelocity = this._performStep(nodeId, maxVelocity); var nodeVelocity = this._performStep(nodeId, maxVelocity);
// stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized
stabilized = nodeVelocity < vminCorrected && stabilized === true;
maxNodeVelocity = Math.max(nodeVelocity, maxNodeVelocity);
nodesPresent = true; nodesPresent = true;
} }
if (nodesPresent === true) {
if (vminCorrected > 0.5 * this.options.maxVelocity) {
this.stabilized = false;
} else {
this.stabilized = stabilized;
}
return;
}
this.stabilized = true;
this.stabilized = maxNodeVelocity <= this.options.minVelocity;
} }
/** /**
@ -33617,7 +33609,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
// enable adaptive timesteps // enable adaptive timesteps
this.adaptive = true;
this.adaptiveTimestep = true;
// this sets the width of all nodes initially which could be required for the avoidOverlap // this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes"); this.body.emitter.emit("_resizeNodes");
@ -33648,7 +33640,6 @@ return /******/ (function(modules) { // webpackBootstrap
var count = 0; var count = 0;
while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) { while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) {
this.physicsTick(); this.physicsTick();
this.stabilizationIterations++;
count++; count++;
} }
@ -39026,8 +39017,8 @@ return /******/ (function(modules) { // webpackBootstrap
} else { } else {
this.body.modules.clustering.clusterOutliers(); this.body.modules.clustering.clusterOutliers();
} }
console.log('levels', levels);
} }
// increase the size of the edges
this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) }); this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) });
} }

+ 1
- 2
lib/network/modules/LayoutEngine.js View File

@ -198,9 +198,8 @@ class LayoutEngine {
else { else {
this.body.modules.clustering.clusterOutliers(); this.body.modules.clustering.clusterOutliers();
} }
console.log('levels', levels)
} }
// increase the size of the edges
this.body.modules.kamadaKawai.setOptions({springLength: Math.max(150,2*startLength)}) this.body.modules.kamadaKawai.setOptions({springLength: Math.max(150,2*startLength)})
} }

+ 14
- 23
lib/network/modules/PhysicsEngine.js View File

@ -20,9 +20,9 @@ class PhysicsEngine {
this.requiresTimeout = true; this.requiresTimeout = true;
this.previousStates = {}; this.previousStates = {};
this.referenceState = {}; this.referenceState = {};
this.adaptive = false;
this.adaptiveTimestep = true;
this.adaptiveCounter = 0; this.adaptiveCounter = 0;
this.adaptiveInterval = 3;
this.adaptiveInterval = 2;
this.freezeCache = {}; this.freezeCache = {};
this.renderTimer = undefined; this.renderTimer = undefined;
@ -69,7 +69,7 @@ class PhysicsEngine {
damping: 0.09 damping: 0.09
}, },
maxVelocity: 50, maxVelocity: 50,
minVelocity: 0.1, // px/s
minVelocity: 0.75, // px/s
solver: 'barnesHut', solver: 'barnesHut',
stabilization: { stabilization: {
enabled: true, enabled: true,
@ -200,7 +200,7 @@ class PhysicsEngine {
this.stabilized = false; this.stabilized = false;
// when visible, adaptivity is disabled. // when visible, adaptivity is disabled.
this.adaptive = false;
//this.adaptiveTimestep = false;
// this sets the width of all nodes initially which could be required for the avoidOverlap // this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes"); this.body.emitter.emit("_resizeNodes");
@ -279,9 +279,10 @@ class PhysicsEngine {
physicsTick() { physicsTick() {
if (this.stabilized === false) { if (this.stabilized === false) {
// adaptivity means the timestep adapts to the situation, only applicable for stabilization // adaptivity means the timestep adapts to the situation, only applicable for stabilization
if (this.adaptive === true) {
if (this.adaptiveTimestep === true) {
this.adaptiveCounter += 1; this.adaptiveCounter += 1;
if (this.adaptiveCounter % this.adaptiveInterval === 0) { // we leave the timestep stable for "interval" iterations. if (this.adaptiveCounter % this.adaptiveInterval === 0) { // we leave the timestep stable for "interval" iterations.
//console.log(this.timestep)
// first the big step and revert. Revert saves the reference state. // first the big step and revert. Revert saves the reference state.
this.timestep = 2 * this.timestep; this.timestep = 2 * this.timestep;
this.calculateForces(); this.calculateForces();
@ -299,20 +300,20 @@ class PhysicsEngine {
// we compare the two steps. if it is acceptable we double the step. // we compare the two steps. if it is acceptable we double the step.
if (this.compare() === true) { if (this.compare() === true) {
this.timestep = 2 * this.timestep;
this.timestep = 1.1 * this.timestep;
} }
else { else {
// if not, we half the step to a minimum of the options timestep. // if not, we half the step to a minimum of the options timestep.
// if the half the timestep is smaller than the options step, we do not reset the counter // if the half the timestep is smaller than the options step, we do not reset the counter
// we assume that the options timestep is stable enough. // we assume that the options timestep is stable enough.
if (0.5 * this.timestep < this.options.timestep) {
if (0.9 * this.timestep < this.options.timestep) {
this.timestep = this.options.timestep; this.timestep = this.options.timestep;
} }
else { else {
// if the timestep was larger than 2 times the option one we check the adaptivity again to ensure // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure
// that large instabilities do not form. // that large instabilities do not form.
this.adaptiveCounter = -1; // check again next iteration this.adaptiveCounter = -1; // check again next iteration
this.timestep = 0.5 * this.timestep;
this.timestep = 0.9 * this.timestep;
} }
} }
} }
@ -454,27 +455,18 @@ class PhysicsEngine {
var nodesPresent = false; var nodesPresent = false;
var nodeIndices = this.physicsBody.physicsNodeIndices; var nodeIndices = this.physicsBody.physicsNodeIndices;
var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9; var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9;
var stabilized = true;
var vminCorrected = this.options.minVelocity / Math.max(this.body.view.scale,0.05);
var maxNodeVelocity = 0;
for (let i = 0; i < nodeIndices.length; i++) { for (let i = 0; i < nodeIndices.length; i++) {
let nodeId = nodeIndices[i]; let nodeId = nodeIndices[i];
let nodeVelocity = this._performStep(nodeId, maxVelocity); let nodeVelocity = this._performStep(nodeId, maxVelocity);
// stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized
stabilized = nodeVelocity < vminCorrected && stabilized === true;
maxNodeVelocity = Math.max(nodeVelocity,maxNodeVelocity);
nodesPresent = true; nodesPresent = true;
} }
if (nodesPresent === true) {
if (vminCorrected > 0.5*this.options.maxVelocity) {
this.stabilized = false;
}
else {
this.stabilized = stabilized;
}
return;
}
this.stabilized = true;
this.stabilized = maxNodeVelocity <= this.options.minVelocity;
} }
@ -588,7 +580,7 @@ class PhysicsEngine {
} }
// enable adaptive timesteps // enable adaptive timesteps
this.adaptive = true;
this.adaptiveTimestep = true;
// this sets the width of all nodes initially which could be required for the avoidOverlap // this sets the width of all nodes initially which could be required for the avoidOverlap
this.body.emitter.emit("_resizeNodes"); this.body.emitter.emit("_resizeNodes");
@ -616,7 +608,6 @@ class PhysicsEngine {
var count = 0; var count = 0;
while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) { while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) {
this.physicsTick(); this.physicsTick();
this.stabilizationIterations++;
count++; count++;
} }

Loading…
Cancel
Save