Browse Source

made the physics calculations a bit more stable (decreasing BarnesHut theta to 0.5 from 0.6), gave nodes a boundingBox that is used for zoomExtent to ensure everything fits.

v3_develop
Alex de Mulder 10 years ago
parent
commit
31aa7fe676
4 changed files with 485 additions and 397 deletions
  1. +425
    -381
      dist/vis.js
  2. +22
    -12
      lib/network/Network.js
  3. +36
    -2
      lib/network/Node.js
  4. +2
    -2
      lib/network/mixins/physics/BarnesHutMixin.js

+ 425
- 381
dist/vis.js View File

@ -106,7 +106,7 @@ return /******/ (function(modules) { // webpackBootstrap
exports.Graph2d = __webpack_require__(42);
exports.timeline = {
DateUtil: __webpack_require__(24),
DataStep: __webpack_require__(44),
DataStep: __webpack_require__(45),
Range: __webpack_require__(21),
stack: __webpack_require__(28),
TimeStep: __webpack_require__(38),
@ -123,7 +123,7 @@ return /******/ (function(modules) { // webpackBootstrap
Component: __webpack_require__(23),
CurrentTime: __webpack_require__(39),
CustomTime: __webpack_require__(41),
DataAxis: __webpack_require__(45),
DataAxis: __webpack_require__(44),
GraphGroup: __webpack_require__(46),
Group: __webpack_require__(27),
BackgroundGroup: __webpack_require__(31),
@ -19638,7 +19638,7 @@ return /******/ (function(modules) { // webpackBootstrap
var DataSet = __webpack_require__(7);
var DataView = __webpack_require__(9);
var Component = __webpack_require__(23);
var DataAxis = __webpack_require__(45);
var DataAxis = __webpack_require__(44);
var GraphGroup = __webpack_require__(46);
var Legend = __webpack_require__(50);
var BarGraphFunctions = __webpack_require__(49);
@ -20631,293 +20631,12 @@ return /******/ (function(modules) { // webpackBootstrap
/***/ },
/* 44 */
/***/ function(module, exports, __webpack_require__) {
/**
* @constructor DataStep
* The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an
* end data point. The class itself determines the best scale (step size) based on the
* provided start Date, end Date, and minimumStep.
*
* If minimumStep is provided, the step size is chosen as close as possible
* to the minimumStep but larger than minimumStep. If minimumStep is not
* provided, the scale is set to 1 DAY.
* The minimumStep should correspond with the onscreen size of about 6 characters
*
* Alternatively, you can set a scale by hand.
* After creation, you can initialize the class by executing first(). Then you
* can iterate from the start date to the end date via next(). You can check if
* the end date is reached with the function hasNext(). After each step, you can
* retrieve the current date via getCurrent().
* The DataStep has scales ranging from milliseconds, seconds, minutes, hours,
* days, to years.
*
* Version: 1.2
*
* @param {Date} [start] The start date, for example new Date(2010, 9, 21)
* or new Date(2010, 9, 21, 23, 45, 00)
* @param {Date} [end] The end date
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) {
// variables
this.current = 0;
this.autoScale = true;
this.stepIndex = 0;
this.step = 1;
this.scale = 1;
this.marginStart;
this.marginEnd;
this.deadSpace = 0;
this.majorSteps = [1, 2, 5, 10];
this.minorSteps = [0.25, 0.5, 1, 2];
this.alignZeros = alignZeros;
this.setRange(start, end, minimumStep, containerHeight, customRange);
}
/**
* Set a new range
* If minimumStep is provided, the step size is chosen as close as possible
* to the minimumStep but larger than minimumStep. If minimumStep is not
* provided, the scale is set to 1 DAY.
* The minimumStep should correspond with the onscreen size of about 6 characters
* @param {Number} [start] The start date and time.
* @param {Number} [end] The end date and time.
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) {
this._start = customRange.min === undefined ? start : customRange.min;
this._end = customRange.max === undefined ? end : customRange.max;
if (this._start == this._end) {
this._start -= 0.75;
this._end += 1;
}
if (this.autoScale == true) {
this.setMinimumStep(minimumStep, containerHeight);
}
this.setFirst(customRange);
};
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.2;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var start = 0;
if (orderOfMagnitude < 0) {
start = orderOfMagnitude;
}
var solutionFound = false;
for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
if (solutionFound == true) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/**
* Round the current date to the first minor date value
* This must be executed once when the current date is set to start Date
*/
DataStep.prototype.setFirst = function(customRange) {
if (customRange === undefined) {
customRange = {};
}
var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min;
var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max;
this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max;
this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min;
// if we need to align the zero's we need to make sure that there is a zero to use.
if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) {
this.marginEnd += this.marginEnd % this.step;
}
this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart;
this.marginRange = this.marginEnd - this.marginStart;
this.current = this.marginEnd;
};
DataStep.prototype.roundToMinor = function(value) {
var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex]));
if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) {
return rounded + (this.scale * this.minorSteps[this.stepIndex]);
}
else {
return rounded;
}
}
/**
* Check if the there is a next step
* @return {boolean} true if the current date has not passed the end date
*/
DataStep.prototype.hasNext = function () {
return (this.current >= this.marginStart);
};
/**
* Do the next step
*/
DataStep.prototype.next = function() {
var prev = this.current;
this.current -= this.step;
// safety mechanism: if current time is still unchanged, move to the end
if (this.current == prev) {
this.current = this._end;
}
};
/**
* Do the next step
*/
DataStep.prototype.previous = function() {
this.current += this.step;
this.marginEnd += this.step;
this.marginRange = this.marginEnd - this.marginStart;
};
/**
* Get the current datetime
* @return {String} current The current date
*/
DataStep.prototype.getCurrent = function(decimals) {
// prevent round-off errors when close to zero
var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current;
var toPrecision = '' + Number(current).toPrecision(5);
// If decimals is specified, then limit or extend the string as required
if(decimals !== undefined && !isNaN(Number(decimals))) {
// If string includes exponent, then we need to add it to the end
var exp = "";
var index = toPrecision.indexOf("e");
if(index != -1) {
// Get the exponent
exp = toPrecision.slice(index);
// Remove the exponent in case we need to zero-extend
toPrecision = toPrecision.slice(0, index);
}
index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf("."));
if(index === -1) {
// No decimal found - if we want decimals, then we need to add it
if(decimals !== 0) {
toPrecision += '.';
}
// Calculate how long the string should be
index = toPrecision.length + decimals;
}
else if(decimals !== 0) {
// Calculate how long the string should be - accounting for the decimal place
index += decimals + 1;
}
if(index > toPrecision.length) {
// We need to add zeros!
for(var cnt = index - toPrecision.length; cnt > 0; cnt--) {
toPrecision += '0';
}
}
else {
// we need to remove characters
toPrecision = toPrecision.slice(0, index);
}
// Add the exponent if there is one
toPrecision += exp;
}
else {
if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) {
// If no decimal is specified, and there are decimal places, remove trailing zeros
for (var i = toPrecision.length - 1; i > 0; i--) {
if (toPrecision[i] == "0") {
toPrecision = toPrecision.slice(0, i);
}
else if (toPrecision[i] == "." || toPrecision[i] == ",") {
toPrecision = toPrecision.slice(0, i);
break;
}
else {
break;
}
}
}
}
return toPrecision;
};
/**
* Snap a date to a rounded value.
* The snap intervals are dependent on the current scale and step.
* @param {Date} date the date to be snapped.
* @return {Date} snappedDate
*/
DataStep.prototype.snap = function(date) {
};
/**
* Check if the current value is a major value (for example when the step
* is DAY, a major value is each first day of the MONTH)
* @return {boolean} true if current date is major, else false.
*/
DataStep.prototype.isMajor = function() {
return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
};
module.exports = DataStep;
/***/ },
/* 45 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(1);
var DOMutil = __webpack_require__(6);
var Component = __webpack_require__(23);
var DataStep = __webpack_require__(44);
var DataStep = __webpack_require__(45);
/**
* A horizontal time axis
@ -21431,125 +21150,406 @@ return /******/ (function(modules) { // webpackBootstrap
};
/**
* Create a minor line for the axis at position y
* @param y
* @param orientation
* @param className
* @param offset
* @param width
* Create a minor line for the axis at position y
* @param y
* @param orientation
* @param className
* @param offset
* @param width
*/
DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
if (this.master == true) {
var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift();
line.className = className;
line.innerHTML = '';
if (orientation == 'left') {
line.style.left = (this.width - offset) + 'px';
}
else {
line.style.right = (this.width - offset) + 'px';
}
line.style.width = width + 'px';
line.style.top = y + 'px';
}
};
/**
* Create a title for the axis
* @private
* @param orientation
*/
DataAxis.prototype._redrawTitle = function (orientation) {
DOMutil.prepareElements(this.DOMelements.title);
// Check if the title is defined for this axes
if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) {
var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);
title.className = 'yAxis title ' + orientation;
title.innerHTML = this.options.title[orientation].text;
// Add style - if provided
if (this.options.title[orientation].style !== undefined) {
util.addCssText(title, this.options.title[orientation].style);
}
if (orientation == 'left') {
title.style.left = this.props.titleCharHeight + 'px';
}
else {
title.style.right = this.props.titleCharHeight + 'px';
}
title.style.width = this.height + 'px';
}
// we need to clean up in case we did not use all elements.
DOMutil.cleanupElements(this.DOMelements.title);
};
/**
* Determine the size of text on the axis (both major and minor axis).
* The size is calculated only once and then cached in this.props.
* @private
*/
DataAxis.prototype._calculateCharSize = function () {
// determine the char width and height on the minor axis
if (!('minorCharHeight' in this.props)) {
var textMinor = document.createTextNode('0');
var measureCharMinor = document.createElement('div');
measureCharMinor.className = 'yAxis minor measure';
measureCharMinor.appendChild(textMinor);
this.dom.frame.appendChild(measureCharMinor);
this.props.minorCharHeight = measureCharMinor.clientHeight;
this.props.minorCharWidth = measureCharMinor.clientWidth;
this.dom.frame.removeChild(measureCharMinor);
}
if (!('majorCharHeight' in this.props)) {
var textMajor = document.createTextNode('0');
var measureCharMajor = document.createElement('div');
measureCharMajor.className = 'yAxis major measure';
measureCharMajor.appendChild(textMajor);
this.dom.frame.appendChild(measureCharMajor);
this.props.majorCharHeight = measureCharMajor.clientHeight;
this.props.majorCharWidth = measureCharMajor.clientWidth;
this.dom.frame.removeChild(measureCharMajor);
}
if (!('titleCharHeight' in this.props)) {
var textTitle = document.createTextNode('0');
var measureCharTitle = document.createElement('div');
measureCharTitle.className = 'yAxis title measure';
measureCharTitle.appendChild(textTitle);
this.dom.frame.appendChild(measureCharTitle);
this.props.titleCharHeight = measureCharTitle.clientHeight;
this.props.titleCharWidth = measureCharTitle.clientWidth;
this.dom.frame.removeChild(measureCharTitle);
}
};
/**
* Snap a date to a rounded value.
* The snap intervals are dependent on the current scale and step.
* @param {Date} date the date to be snapped.
* @return {Date} snappedDate
*/
DataAxis.prototype.snap = function(date) {
return this.step.snap(date);
};
module.exports = DataAxis;
/***/ },
/* 45 */
/***/ function(module, exports, __webpack_require__) {
/**
* @constructor DataStep
* The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an
* end data point. The class itself determines the best scale (step size) based on the
* provided start Date, end Date, and minimumStep.
*
* If minimumStep is provided, the step size is chosen as close as possible
* to the minimumStep but larger than minimumStep. If minimumStep is not
* provided, the scale is set to 1 DAY.
* The minimumStep should correspond with the onscreen size of about 6 characters
*
* Alternatively, you can set a scale by hand.
* After creation, you can initialize the class by executing first(). Then you
* can iterate from the start date to the end date via next(). You can check if
* the end date is reached with the function hasNext(). After each step, you can
* retrieve the current date via getCurrent().
* The DataStep has scales ranging from milliseconds, seconds, minutes, hours,
* days, to years.
*
* Version: 1.2
*
* @param {Date} [start] The start date, for example new Date(2010, 9, 21)
* or new Date(2010, 9, 21, 23, 45, 00)
* @param {Date} [end] The end date
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) {
// variables
this.current = 0;
this.autoScale = true;
this.stepIndex = 0;
this.step = 1;
this.scale = 1;
this.marginStart;
this.marginEnd;
this.deadSpace = 0;
this.majorSteps = [1, 2, 5, 10];
this.minorSteps = [0.25, 0.5, 1, 2];
this.alignZeros = alignZeros;
this.setRange(start, end, minimumStep, containerHeight, customRange);
}
/**
* Set a new range
* If minimumStep is provided, the step size is chosen as close as possible
* to the minimumStep but larger than minimumStep. If minimumStep is not
* provided, the scale is set to 1 DAY.
* The minimumStep should correspond with the onscreen size of about 6 characters
* @param {Number} [start] The start date and time.
* @param {Number} [end] The end date and time.
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
*/
DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) {
this._start = customRange.min === undefined ? start : customRange.min;
this._end = customRange.max === undefined ? end : customRange.max;
if (this._start == this._end) {
this._start -= 0.75;
this._end += 1;
}
if (this.autoScale == true) {
this.setMinimumStep(minimumStep, containerHeight);
}
this.setFirst(customRange);
};
/**
* Automatically determine the scale that bests fits the provided minimum step
* @param {Number} [minimumStep] The minimum step size in milliseconds
*/
DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
if (this.master == true) {
var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift();
line.className = className;
line.innerHTML = '';
DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
// round to floor
var size = this._end - this._start;
var safeSize = size * 1.2;
var minimumStepValue = minimumStep * (safeSize / containerHeight);
var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10);
if (orientation == 'left') {
line.style.left = (this.width - offset) + 'px';
var minorStepIdx = -1;
var magnitudefactor = Math.pow(10,orderOfMagnitude);
var start = 0;
if (orderOfMagnitude < 0) {
start = orderOfMagnitude;
}
var solutionFound = false;
for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) {
magnitudefactor = Math.pow(10,i);
for (var j = 0; j < this.minorSteps.length; j++) {
var stepSize = magnitudefactor * this.minorSteps[j];
if (stepSize >= minimumStepValue) {
solutionFound = true;
minorStepIdx = j;
break;
}
}
else {
line.style.right = (this.width - offset) + 'px';
if (solutionFound == true) {
break;
}
line.style.width = width + 'px';
line.style.top = y + 'px';
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];
};
/**
* Create a title for the axis
* @private
* @param orientation
* Round the current date to the first minor date value
* This must be executed once when the current date is set to start Date
*/
DataAxis.prototype._redrawTitle = function (orientation) {
DOMutil.prepareElements(this.DOMelements.title);
// Check if the title is defined for this axes
if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) {
var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);
title.className = 'yAxis title ' + orientation;
title.innerHTML = this.options.title[orientation].text;
DataStep.prototype.setFirst = function(customRange) {
if (customRange === undefined) {
customRange = {};
}
// Add style - if provided
if (this.options.title[orientation].style !== undefined) {
util.addCssText(title, this.options.title[orientation].style);
}
var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min;
var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max;
if (orientation == 'left') {
title.style.left = this.props.titleCharHeight + 'px';
}
else {
title.style.right = this.props.titleCharHeight + 'px';
}
this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max;
this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min;
title.style.width = this.height + 'px';
// if we need to align the zero's we need to make sure that there is a zero to use.
if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) {
this.marginEnd += this.marginEnd % this.step;
}
// we need to clean up in case we did not use all elements.
DOMutil.cleanupElements(this.DOMelements.title);
};
this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart;
this.marginRange = this.marginEnd - this.marginStart;
this.current = this.marginEnd;
};
DataStep.prototype.roundToMinor = function(value) {
var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex]));
if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) {
return rounded + (this.scale * this.minorSteps[this.stepIndex]);
}
else {
return rounded;
}
}
/**
* Determine the size of text on the axis (both major and minor axis).
* The size is calculated only once and then cached in this.props.
* @private
* Check if the there is a next step
* @return {boolean} true if the current date has not passed the end date
*/
DataAxis.prototype._calculateCharSize = function () {
// determine the char width and height on the minor axis
if (!('minorCharHeight' in this.props)) {
var textMinor = document.createTextNode('0');
var measureCharMinor = document.createElement('div');
measureCharMinor.className = 'yAxis minor measure';
measureCharMinor.appendChild(textMinor);
this.dom.frame.appendChild(measureCharMinor);
DataStep.prototype.hasNext = function () {
return (this.current >= this.marginStart);
};
this.props.minorCharHeight = measureCharMinor.clientHeight;
this.props.minorCharWidth = measureCharMinor.clientWidth;
/**
* Do the next step
*/
DataStep.prototype.next = function() {
var prev = this.current;
this.current -= this.step;
this.dom.frame.removeChild(measureCharMinor);
// safety mechanism: if current time is still unchanged, move to the end
if (this.current == prev) {
this.current = this._end;
}
};
if (!('majorCharHeight' in this.props)) {
var textMajor = document.createTextNode('0');
var measureCharMajor = document.createElement('div');
measureCharMajor.className = 'yAxis major measure';
measureCharMajor.appendChild(textMajor);
this.dom.frame.appendChild(measureCharMajor);
this.props.majorCharHeight = measureCharMajor.clientHeight;
this.props.majorCharWidth = measureCharMajor.clientWidth;
/**
* Do the next step
*/
DataStep.prototype.previous = function() {
this.current += this.step;
this.marginEnd += this.step;
this.marginRange = this.marginEnd - this.marginStart;
};
this.dom.frame.removeChild(measureCharMajor);
}
if (!('titleCharHeight' in this.props)) {
var textTitle = document.createTextNode('0');
var measureCharTitle = document.createElement('div');
measureCharTitle.className = 'yAxis title measure';
measureCharTitle.appendChild(textTitle);
this.dom.frame.appendChild(measureCharTitle);
this.props.titleCharHeight = measureCharTitle.clientHeight;
this.props.titleCharWidth = measureCharTitle.clientWidth;
/**
* Get the current datetime
* @return {String} current The current date
*/
DataStep.prototype.getCurrent = function(decimals) {
// prevent round-off errors when close to zero
var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current;
var toPrecision = '' + Number(current).toPrecision(5);
this.dom.frame.removeChild(measureCharTitle);
// If decimals is specified, then limit or extend the string as required
if(decimals !== undefined && !isNaN(Number(decimals))) {
// If string includes exponent, then we need to add it to the end
var exp = "";
var index = toPrecision.indexOf("e");
if(index != -1) {
// Get the exponent
exp = toPrecision.slice(index);
// Remove the exponent in case we need to zero-extend
toPrecision = toPrecision.slice(0, index);
}
index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf("."));
if(index === -1) {
// No decimal found - if we want decimals, then we need to add it
if(decimals !== 0) {
toPrecision += '.';
}
// Calculate how long the string should be
index = toPrecision.length + decimals;
}
else if(decimals !== 0) {
// Calculate how long the string should be - accounting for the decimal place
index += decimals + 1;
}
if(index > toPrecision.length) {
// We need to add zeros!
for(var cnt = index - toPrecision.length; cnt > 0; cnt--) {
toPrecision += '0';
}
}
else {
// we need to remove characters
toPrecision = toPrecision.slice(0, index);
}
// Add the exponent if there is one
toPrecision += exp;
}
else {
if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) {
// If no decimal is specified, and there are decimal places, remove trailing zeros
for (var i = toPrecision.length - 1; i > 0; i--) {
if (toPrecision[i] == "0") {
toPrecision = toPrecision.slice(0, i);
}
else if (toPrecision[i] == "." || toPrecision[i] == ",") {
toPrecision = toPrecision.slice(0, i);
break;
}
else {
break;
}
}
}
}
return toPrecision;
};
/**
* Snap a date to a rounded value.
* The snap intervals are dependent on the current scale and step.
* @param {Date} date the date to be snapped.
* @return {Date} snappedDate
*/
DataAxis.prototype.snap = function(date) {
return this.step.snap(date);
DataStep.prototype.snap = function(date) {
};
module.exports = DataAxis;
/**
* Check if the current value is a major value (for example when the step
* is DAY, a major value is each first day of the MONTH)
* @return {boolean} true if current date is major, else false.
*/
DataStep.prototype.isMajor = function() {
return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
};
module.exports = DataStep;
/***/ },
@ -22593,7 +22593,7 @@ return /******/ (function(modules) { // webpackBootstrap
physics: {
barnesHut: {
enabled: true,
theta: 1 / 0.6, // inverted to save time during calculation
thetaInverted: 1 / 0.5, // inverted to save time during calculation
gravitationalConstant: -2000,
centralGravity: 0.3,
springLength: 95,
@ -22864,10 +22864,10 @@ return /******/ (function(modules) { // webpackBootstrap
for (var nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
if (minX > (node.x)) {minX = node.x;}
if (maxX < (node.x)) {maxX = node.x;}
if (minY > (node.y)) {minY = node.y;}
if (maxY < (node.y)) {maxY = node.y;}
if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;}
if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;}
if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.bottom;}
if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.top;}
}
}
if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
@ -22895,6 +22895,8 @@ return /******/ (function(modules) { // webpackBootstrap
* @param {Boolean} [disableStart] | If true, start is not called.
*/
Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) {
this._redraw(true);
if (initialZoom === undefined) {
initialZoom = false;
}
@ -24221,9 +24223,10 @@ return /******/ (function(modules) { // webpackBootstrap
/**
* Redraw the network with the current data
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private
*/
Network.prototype._redraw = function() {
Network.prototype._redraw = function(hidden) {
var ctx = this.frame.canvas.getContext('2d');
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
@ -24247,18 +24250,21 @@ return /******/ (function(modules) { // webpackBootstrap
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio)
};
this._doInAllSectors("_drawAllSectorNodes",ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges",ctx);
if (!(hidden == true)) {
this._doInAllSectors("_drawAllSectorNodes", 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);
if (!(hidden == true)) {
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes", ctx);
}
}
// this._doInSupportSector("_drawNodes",ctx,true);
@ -24266,6 +24272,10 @@ return /******/ (function(modules) { // webpackBootstrap
// restore original scaling and translation
ctx.restore();
if (hidden == true) {
ctx.clearRect(0, 0, w, h);
}
};
/**
@ -26193,8 +26203,8 @@ return /******/ (function(modules) { // webpackBootstrap
this.level = -1;
this.preassignedLevel = false;
this.hierarchyEnumerated = false;
this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached
this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached
this.boundingBox = {top:0, left:0, right:0, bottom:0};
this.imagelist = imagelist;
this.grouplist = grouplist;
@ -26755,6 +26765,11 @@ return /******/ (function(modules) { // webpackBootstrap
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -26804,6 +26819,11 @@ return /******/ (function(modules) { // webpackBootstrap
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -26855,6 +26875,11 @@ return /******/ (function(modules) { // webpackBootstrap
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
this._label(ctx, this.label, this.x, this.y);
};
@ -26906,6 +26931,12 @@ return /******/ (function(modules) { // webpackBootstrap
ctx.ellipse(this.left, this.top, this.width, this.height);
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -26983,8 +27014,16 @@ return /******/ (function(modules) { // webpackBootstrap
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
if (this.label) {
this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top',true);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left)
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width)
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height)
}
};
@ -27009,6 +27048,11 @@ return /******/ (function(modules) { // webpackBootstrap
this.top = this.y - this.height / 2;
this._label(ctx, this.label, this.x, this.y);
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
};
@ -29712,9 +29756,9 @@ return /******/ (function(modules) { // webpackBootstrap
distance = Math.sqrt(dx * dx + dy * dy);
// BarnesHut condition
// original condition : s/d < theta = passed === d/s > 1/theta = passed
// original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed
// calcSize = 1/s --> d * 1/s > 1/theta = passed
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) {
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) {
// duplicate code to reduce function calls to speed up program
if (distance == 0) {
distance = 0.1*Math.random();

+ 22
- 12
lib/network/Network.js View File

@ -114,7 +114,7 @@ function Network (container, data, options) {
physics: {
barnesHut: {
enabled: true,
theta: 1 / 0.6, // inverted to save time during calculation
thetaInverted: 1 / 0.5, // inverted to save time during calculation
gravitationalConstant: -2000,
centralGravity: 0.3,
springLength: 95,
@ -385,10 +385,10 @@ Network.prototype._getRange = function() {
for (var nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
node = this.nodes[nodeId];
if (minX > (node.x)) {minX = node.x;}
if (maxX < (node.x)) {maxX = node.x;}
if (minY > (node.y)) {minY = node.y;}
if (maxY < (node.y)) {maxY = node.y;}
if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;}
if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;}
if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.bottom;}
if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.top;}
}
}
if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
@ -416,6 +416,8 @@ Network.prototype._findCenter = function(range) {
* @param {Boolean} [disableStart] | If true, start is not called.
*/
Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) {
this._redraw(true);
if (initialZoom === undefined) {
initialZoom = false;
}
@ -1742,9 +1744,10 @@ Network.prototype.redraw = function() {
/**
* Redraw the network with the current data
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private
*/
Network.prototype._redraw = function() {
Network.prototype._redraw = function(hidden) {
var ctx = this.frame.canvas.getContext('2d');
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
@ -1768,18 +1771,21 @@ Network.prototype._redraw = function() {
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio)
};
this._doInAllSectors("_drawAllSectorNodes",ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges",ctx);
if (!(hidden == true)) {
this._doInAllSectors("_drawAllSectorNodes", 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);
if (!(hidden == true)) {
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes", ctx);
}
}
// this._doInSupportSector("_drawNodes",ctx,true);
@ -1787,6 +1793,10 @@ Network.prototype._redraw = function() {
// restore original scaling and translation
ctx.restore();
if (hidden == true) {
ctx.clearRect(0, 0, w, h);
}
};
/**

+ 36
- 2
lib/network/Node.js View File

@ -53,8 +53,8 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.level = -1;
this.preassignedLevel = false;
this.hierarchyEnumerated = false;
this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached
this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached
this.boundingBox = {top:0, left:0, right:0, bottom:0};
this.imagelist = imagelist;
this.grouplist = grouplist;
@ -615,6 +615,11 @@ Node.prototype._drawBox = function (ctx) {
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -664,6 +669,11 @@ Node.prototype._drawDatabase = function (ctx) {
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -715,6 +725,11 @@ Node.prototype._drawCircle = function (ctx) {
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
this._label(ctx, this.label, this.x, this.y);
};
@ -766,6 +781,12 @@ Node.prototype._drawEllipse = function (ctx) {
ctx.ellipse(this.left, this.top, this.width, this.height);
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
this._label(ctx, this.label, this.x, this.y);
};
@ -843,8 +864,16 @@ Node.prototype._drawShape = function (ctx, shape) {
ctx.fill();
ctx.stroke();
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;
this.boundingBox.right = this.x + this.options.radius;
this.boundingBox.bottom = this.y + this.options.radius;
if (this.label) {
this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'top',true);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left)
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width)
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height)
}
};
@ -869,6 +898,11 @@ Node.prototype._drawText = function (ctx) {
this.top = this.y - this.height / 2;
this._label(ctx, this.label, this.x, this.y);
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
};

+ 2
- 2
lib/network/mixins/physics/BarnesHutMixin.js View File

@ -49,9 +49,9 @@ exports._getForceContribution = function(parentBranch,node) {
distance = Math.sqrt(dx * dx + dy * dy);
// BarnesHut condition
// original condition : s/d < theta = passed === d/s > 1/theta = passed
// original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed
// calcSize = 1/s --> d * 1/s > 1/theta = passed
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) {
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) {
// duplicate code to reduce function calls to speed up program
if (distance == 0) {
distance = 0.1*Math.random();

Loading…
Cancel
Save