|
@ -4,8 +4,8 @@ |
|
|
* |
|
|
* |
|
|
* A dynamic, browser-based visualization library. |
|
|
* A dynamic, browser-based visualization library. |
|
|
* |
|
|
* |
|
|
* @version @@version |
|
|
|
|
|
* @date @@date |
|
|
|
|
|
|
|
|
* @version 0.7.5-SNAPSHOT |
|
|
|
|
|
* @date 2014-04-29 |
|
|
* |
|
|
* |
|
|
* @license |
|
|
* @license |
|
|
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
|
|
|
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
|
|
@ -4772,7 +4772,7 @@ function ItemSet(backgroundPanel, axisPanel, options) { |
|
|
byStart: [], |
|
|
byStart: [], |
|
|
byEnd: [] |
|
|
byEnd: [] |
|
|
}; |
|
|
}; |
|
|
this.systemLoaded = false; |
|
|
|
|
|
|
|
|
// this.systemLoaded = false;
|
|
|
this.visibleItems = []; // visible, ordered items
|
|
|
this.visibleItems = []; // visible, ordered items
|
|
|
this.selection = []; // list with the ids of all selected nodes
|
|
|
this.selection = []; // list with the ids of all selected nodes
|
|
|
this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
|
|
|
this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
|
|
@ -4999,7 +4999,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) { |
|
|
|
|
|
|
|
|
if (high == 0) {guess = -1;} |
|
|
if (high == 0) {guess = -1;} |
|
|
else if (high == 1) { |
|
|
else if (high == 1) { |
|
|
if ((array[guess].data[byTime] > this.range.start - interval) && (array[guess].data[byTime] < this.range.end + interval)) { |
|
|
|
|
|
|
|
|
if ((array[guess].data[byTime] > this.range.start - interval) && (array[guess].data[byTime] < this.range.end)) { |
|
|
guess = 0; |
|
|
guess = 0; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
@ -5009,7 +5009,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) { |
|
|
else { |
|
|
else { |
|
|
high -= 1; |
|
|
high -= 1; |
|
|
while (found == false) { |
|
|
while (found == false) { |
|
|
if ((array[guess].data[byTime] > this.range.start - interval) && (array[guess].data[byTime] < this.range.end + interval)) { |
|
|
|
|
|
|
|
|
if ((array[guess].data[byTime] > this.range.start - interval) && (array[guess].data[byTime] < this.range.end)) { |
|
|
found = true; |
|
|
found = true; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
@ -5034,6 +5034,48 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) { |
|
|
return guess; |
|
|
return guess; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this function checks if an item is invisible. If it is NOT we make it visible and add it to the global visible items. If it is, return true. |
|
|
|
|
|
* |
|
|
|
|
|
* @param {itemRange | itemPoint | itemBox} item |
|
|
|
|
|
* @returns {boolean} |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
|
|
|
ItemSet.prototype._checkIfInvisible = function _checkIfInvisible(item) { |
|
|
|
|
|
if (item.isVisible(this.range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
if (this.visibleItems.indexOf(item) == -1) { |
|
|
|
|
|
this.visibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this function is very similar to the _checkIfInvisible() but it does not return booleans, hides the item if it should not be seen and always adds to the visibleItems. |
|
|
|
|
|
* this one is for brute forcing and hiding. |
|
|
|
|
|
* |
|
|
|
|
|
* @param {itemRange | itemPoint | itemBox} item |
|
|
|
|
|
* @param {array} visibleItems |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
|
|
|
ItemSet.prototype._checkIfVisible = function _checkIfVisible(item, visibleItems) { |
|
|
|
|
|
if (item.isVisible(this.range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
// reposition item horizontally
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
visibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (item.displayed) item.hide(); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Repaint the component |
|
|
* Repaint the component |
|
|
* @return {boolean} Returns true if the component is resized |
|
|
* @return {boolean} Returns true if the component is resized |
|
@ -5056,30 +5098,20 @@ ItemSet.prototype.repaint = function repaint() { |
|
|
|
|
|
|
|
|
var newVisibleItems = []; |
|
|
var newVisibleItems = []; |
|
|
var item; |
|
|
var item; |
|
|
var range = this.range; |
|
|
|
|
|
var orderedItems = this.orderedItems; |
|
|
var orderedItems = this.orderedItems; |
|
|
|
|
|
|
|
|
// first check if the items that were in view previously are still in view.
|
|
|
// first check if the items that were in view previously are still in view.
|
|
|
// this handles the case for the ItemRange that is both before and after the current one.
|
|
|
// this handles the case for the ItemRange that is both before and after the current one.
|
|
|
if (this.visibleItems.length > 0) { |
|
|
if (this.visibleItems.length > 0) { |
|
|
for (var i = 0; i < this.visibleItems.length; i++) { |
|
|
for (var i = 0; i < this.visibleItems.length; i++) { |
|
|
item = this.visibleItems[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
// reposition item horizontally
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
|
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (item.displayed) item.hide(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
this._checkIfVisible(this.visibleItems[i],newVisibleItems); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
this.visibleItems = newVisibleItems; |
|
|
|
|
|
|
|
|
// If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime)
|
|
|
// If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime)
|
|
|
if (newVisibleItems.length == 0) {var initialPosByStart = this._binarySearch(false);} |
|
|
|
|
|
else {var initialPosByStart = orderedItems.byStart.indexOf(newVisibleItems[0]);} |
|
|
|
|
|
|
|
|
if (this.visibleItems.length == 0) {var initialPosByStart = this._binarySearch(false);} |
|
|
|
|
|
else {var initialPosByStart = orderedItems.byStart.indexOf(this.visibleItems[0]);} |
|
|
|
|
|
|
|
|
// use visible search to find a visible ItemRange (only based on endTime)
|
|
|
// use visible search to find a visible ItemRange (only based on endTime)
|
|
|
var initialPosByEnd = this._binarySearch(true); |
|
|
var initialPosByEnd = this._binarySearch(true); |
|
@ -5087,96 +5119,23 @@ ItemSet.prototype.repaint = function repaint() { |
|
|
// if we found a initial ID to use, trace it up and down until we meet an invisible item.
|
|
|
// if we found a initial ID to use, trace it up and down until we meet an invisible item.
|
|
|
if (initialPosByStart != -1) { |
|
|
if (initialPosByStart != -1) { |
|
|
for (var i = initialPosByStart; i >= 0; i--) { |
|
|
for (var i = initialPosByStart; i >= 0; i--) { |
|
|
|
|
|
|
|
|
item = orderedItems.byStart[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
if (newVisibleItems.indexOf(item) == -1) { |
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this._checkIfInvisible(orderedItems.byStart[i])) {break;} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// and up
|
|
|
|
|
|
for (var i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { |
|
|
for (var i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { |
|
|
item = orderedItems.byStart[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
if (newVisibleItems.indexOf(item) == -1) { |
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this._checkIfInvisible(orderedItems.byStart[i])) {break;} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// if we found a initial ID to use, trace it up and down until we meet an invisible item.
|
|
|
// if we found a initial ID to use, trace it up and down until we meet an invisible item.
|
|
|
if (initialPosByEnd != -1) { |
|
|
if (initialPosByEnd != -1) { |
|
|
for (var i = initialPosByEnd; i >= 0; i--) { |
|
|
for (var i = initialPosByEnd; i >= 0; i--) { |
|
|
item = orderedItems.byEnd[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
|
|
|
|
|
|
// reposition item horizontally
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
if (newVisibleItems.indexOf(item) == -1) { |
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this._checkIfInvisible(orderedItems.byEnd[i])) {break;} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// and up
|
|
|
|
|
|
for (var i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { |
|
|
for (var i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { |
|
|
item = orderedItems.byEnd[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
|
|
|
|
|
|
// reposition item horizontally
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
if (newVisibleItems.indexOf(item) == -1) { |
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!this.systemLoaded) { |
|
|
|
|
|
// initial setup is brute force all the ranged items;
|
|
|
|
|
|
// TODO: implement this in the onUpdate function to only load the new items.
|
|
|
|
|
|
for (var i = 0; i < orderedItems.byEnd.length; i++) { |
|
|
|
|
|
item = orderedItems.byEnd[i]; |
|
|
|
|
|
if (item.isVisible(range)) { |
|
|
|
|
|
if (!item.displayed) item.show(); |
|
|
|
|
|
|
|
|
|
|
|
// reposition item horizontally
|
|
|
|
|
|
item.repositionX(); |
|
|
|
|
|
|
|
|
|
|
|
newVisibleItems.push(item); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (item.displayed) item.hide(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this._checkIfInvisible(orderedItems.byEnd[i])) {break;} |
|
|
} |
|
|
} |
|
|
this.systemLoaded = true; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.visibleItems = newVisibleItems; |
|
|
|
|
|
|
|
|
|
|
|
// reposition visible items vertically
|
|
|
// reposition visible items vertically
|
|
|
//this.stack.order(this.visibleItems); // TODO: improve ordering
|
|
|
//this.stack.order(this.visibleItems); // TODO: improve ordering
|
|
|
var force = this.stackDirty || zoomed; // force re-stacking of all items if true
|
|
|
var force = this.stackDirty || zoomed; // force re-stacking of all items if true
|
|
@ -5372,10 +5331,13 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
me.items[id] = item; |
|
|
me.items[id] = item; |
|
|
|
|
|
if (type == 'range') { |
|
|
|
|
|
me._checkIfVisible(item,this.visibleItems); |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this._order(); |
|
|
this._order(); |
|
|
this.systemLoaded = false; |
|
|
|
|
|
|
|
|
// this.systemLoaded = false;
|
|
|
this.stackDirty = true; // force re-stacking of all items next repaint
|
|
|
this.stackDirty = true; // force re-stacking of all items next repaint
|
|
|
this.emit('change'); |
|
|
this.emit('change'); |
|
|
}; |
|
|
}; |
|
@ -7816,7 +7778,7 @@ Timeline.prototype._onAddItem = function (event) { |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
// add item
|
|
|
// add item
|
|
|
var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame); |
|
|
|
|
|
|
|
|
var xAbs = vis.util.getAbsoluteLeft(this.contentPanel.frame); |
|
|
var x = event.gesture.center.pageX - xAbs; |
|
|
var x = event.gesture.center.pageX - xAbs; |
|
|
var newItem = { |
|
|
var newItem = { |
|
|
start: this.timeAxis.snap(this._toTime(x)), |
|
|
start: this.timeAxis.snap(this._toTime(x)), |
|
@ -9312,6 +9274,7 @@ Node.prototype.discreteStep = function(interval) { |
|
|
/** |
|
|
/** |
|
|
* Perform one discrete step for the node |
|
|
* Perform one discrete step for the node |
|
|
* @param {number} interval Time interval in seconds |
|
|
* @param {number} interval Time interval in seconds |
|
|
|
|
|
* @param {number} maxVelocity The speed limit imposed on the velocity |
|
|
*/ |
|
|
*/ |
|
|
Node.prototype.discreteStepLimited = function(interval, maxVelocity) { |
|
|
Node.prototype.discreteStepLimited = function(interval, maxVelocity) { |
|
|
if (!this.xFixed) { |
|
|
if (!this.xFixed) { |
|
@ -10019,7 +9982,7 @@ Edge.prototype.setProperties = function(properties, constants) { |
|
|
this.customLength = true;} |
|
|
this.customLength = true;} |
|
|
|
|
|
|
|
|
// scale the arrow
|
|
|
// scale the arrow
|
|
|
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor}; |
|
|
|
|
|
|
|
|
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;} |
|
|
|
|
|
|
|
|
// Added to support dashed lines
|
|
|
// Added to support dashed lines
|
|
|
// David Jordan
|
|
|
// David Jordan
|
|
@ -11419,6 +11382,13 @@ var physicsMixin = { |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* This overwrites the this.constants. |
|
|
|
|
|
* |
|
|
|
|
|
* @param constantsVariableName |
|
|
|
|
|
* @param value |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
_overWriteGraphConstants: function (constantsVariableName, value) { |
|
|
_overWriteGraphConstants: function (constantsVariableName, value) { |
|
|
var nameArray = constantsVariableName.split("_"); |
|
|
var nameArray = constantsVariableName.split("_"); |
|
|
if (nameArray.length == 1) { |
|
|
if (nameArray.length == 1) { |
|
@ -11433,6 +11403,9 @@ var physicsMixin = { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. |
|
|
|
|
|
*/ |
|
|
function graphToggleSmoothCurves () { |
|
|
function graphToggleSmoothCurves () { |
|
|
this.constants.smoothCurves = !this.constants.smoothCurves; |
|
|
this.constants.smoothCurves = !this.constants.smoothCurves; |
|
|
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); |
|
|
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); |
|
@ -11442,6 +11415,10 @@ function graphToggleSmoothCurves () { |
|
|
this._configureSmoothCurves(false); |
|
|
this._configureSmoothCurves(false); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this function is used to scramble the nodes |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
function graphRepositionNodes () { |
|
|
function graphRepositionNodes () { |
|
|
for (var nodeId in this.calculationNodes) { |
|
|
for (var nodeId in this.calculationNodes) { |
|
|
if (this.calculationNodes.hasOwnProperty(nodeId)) { |
|
|
if (this.calculationNodes.hasOwnProperty(nodeId)) { |
|
@ -11459,6 +11436,9 @@ function graphRepositionNodes () { |
|
|
this.start(); |
|
|
this.start(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this is used to generate an options file from the playing with physics system. |
|
|
|
|
|
*/ |
|
|
function graphGenerateOptions () { |
|
|
function graphGenerateOptions () { |
|
|
var options = "No options are required, default values used."; |
|
|
var options = "No options are required, default values used."; |
|
|
var optionsSpecific = []; |
|
|
var optionsSpecific = []; |
|
@ -11556,7 +11536,10 @@ function graphGenerateOptions () { |
|
|
|
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this is used to switch between barnesHut, repulsion and hierarchical. |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
function switchConfigurations () { |
|
|
function switchConfigurations () { |
|
|
var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; |
|
|
var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; |
|
|
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; |
|
|
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; |
|
@ -11595,6 +11578,14 @@ function switchConfigurations () { |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this generates the ranges depending on the iniital values. |
|
|
|
|
|
* |
|
|
|
|
|
* @param id |
|
|
|
|
|
* @param map |
|
|
|
|
|
* @param constantsVariableName |
|
|
|
|
|
*/ |
|
|
function showValueOfRange (id,map,constantsVariableName) { |
|
|
function showValueOfRange (id,map,constantsVariableName) { |
|
|
var valueId = id + "_value"; |
|
|
var valueId = id + "_value"; |
|
|
var rangeValue = document.getElementById(id).value; |
|
|
var rangeValue = document.getElementById(id).value; |
|
@ -11841,6 +11832,13 @@ var barnesHutMixin = { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* this updates the mass of a branch. this is increased by adding a node. |
|
|
|
|
|
* |
|
|
|
|
|
* @param parentBranch |
|
|
|
|
|
* @param node |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
_updateBranchMass : function(parentBranch, node) { |
|
|
_updateBranchMass : function(parentBranch, node) { |
|
|
var totalMass = parentBranch.mass + node.mass; |
|
|
var totalMass = parentBranch.mass + node.mass; |
|
|
var totalMassInv = 1/totalMass; |
|
|
var totalMassInv = 1/totalMass; |
|
@ -11858,6 +11856,14 @@ var barnesHutMixin = { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* determine in which branch the node will be placed. |
|
|
|
|
|
* |
|
|
|
|
|
* @param parentBranch |
|
|
|
|
|
* @param node |
|
|
|
|
|
* @param skipMassUpdate |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
_placeInTree : function(parentBranch,node,skipMassUpdate) { |
|
|
_placeInTree : function(parentBranch,node,skipMassUpdate) { |
|
|
if (skipMassUpdate != true || skipMassUpdate === undefined) { |
|
|
if (skipMassUpdate != true || skipMassUpdate === undefined) { |
|
|
// update the mass of the branch.
|
|
|
// update the mass of the branch.
|
|
@ -11883,6 +11889,14 @@ var barnesHutMixin = { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* actually place the node in a region (or branch) |
|
|
|
|
|
* |
|
|
|
|
|
* @param parentBranch |
|
|
|
|
|
* @param node |
|
|
|
|
|
* @param region |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
_placeInRegion : function(parentBranch,node,region) { |
|
|
_placeInRegion : function(parentBranch,node,region) { |
|
|
switch (parentBranch.children[region].childrenCount) { |
|
|
switch (parentBranch.children[region].childrenCount) { |
|
|
case 0: // place node here
|
|
|
case 0: // place node here
|
|
@ -12249,7 +12263,7 @@ var HierarchicalLayoutMixin = { |
|
|
*/ |
|
|
*/ |
|
|
_getDistribution : function() { |
|
|
_getDistribution : function() { |
|
|
var distribution = {}; |
|
|
var distribution = {}; |
|
|
var nodeId, node; |
|
|
|
|
|
|
|
|
var nodeId, node, level; |
|
|
|
|
|
|
|
|
// we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time.
|
|
|
// we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time.
|
|
|
// the fix of X is removed after the x value has been set.
|
|
|
// the fix of X is removed after the x value has been set.
|
|
@ -12274,7 +12288,7 @@ var HierarchicalLayoutMixin = { |
|
|
|
|
|
|
|
|
// determine the largest amount of nodes of all levels
|
|
|
// determine the largest amount of nodes of all levels
|
|
|
var maxCount = 0; |
|
|
var maxCount = 0; |
|
|
for (var level in distribution) { |
|
|
|
|
|
|
|
|
for (level in distribution) { |
|
|
if (distribution.hasOwnProperty(level)) { |
|
|
if (distribution.hasOwnProperty(level)) { |
|
|
if (maxCount < distribution[level].amount) { |
|
|
if (maxCount < distribution[level].amount) { |
|
|
maxCount = distribution[level].amount; |
|
|
maxCount = distribution[level].amount; |
|
@ -12283,7 +12297,7 @@ var HierarchicalLayoutMixin = { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// set the initial position and spacing of each nodes accordingly
|
|
|
// set the initial position and spacing of each nodes accordingly
|
|
|
for (var level in distribution) { |
|
|
|
|
|
|
|
|
for (level in distribution) { |
|
|
if (distribution.hasOwnProperty(level)) { |
|
|
if (distribution.hasOwnProperty(level)) { |
|
|
distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; |
|
|
distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; |
|
|
distribution[level].nodeSpacing /= (distribution[level].amount + 1); |
|
|
distribution[level].nodeSpacing /= (distribution[level].amount + 1); |
|
@ -12725,8 +12739,6 @@ var manipulationMixin = { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Adds a node on the specified location |
|
|
* Adds a node on the specified location |
|
|
* |
|
|
|
|
|
* @param {Object} pointer |
|
|
|
|
|
*/ |
|
|
*/ |
|
|
_addNode : function() { |
|
|
_addNode : function() { |
|
|
if (this._selectionIsEmpty() && this.editMode == true) { |
|
|
if (this._selectionIsEmpty() && this.editMode == true) { |
|
@ -13561,6 +13573,7 @@ var ClusterMixin = { |
|
|
* @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn |
|
|
* @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn |
|
|
* @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters |
|
|
* @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters |
|
|
* @param {Boolean} force | enabled or disable forcing |
|
|
* @param {Boolean} force | enabled or disable forcing |
|
|
|
|
|
* @param {Boolean} doNotStart | if true do not call start |
|
|
* |
|
|
* |
|
|
*/ |
|
|
*/ |
|
|
updateClusters : function(zoomDirection,recursive,force,doNotStart) { |
|
|
updateClusters : function(zoomDirection,recursive,force,doNotStart) { |
|
@ -14411,9 +14424,10 @@ var ClusterMixin = { |
|
|
var maxLevel = 0; |
|
|
var maxLevel = 0; |
|
|
var minLevel = 1e9; |
|
|
var minLevel = 1e9; |
|
|
var clusterLevel = 0; |
|
|
var clusterLevel = 0; |
|
|
|
|
|
var nodeId; |
|
|
|
|
|
|
|
|
// we loop over all nodes in the list
|
|
|
// we loop over all nodes in the list
|
|
|
for (var nodeId in this.nodes) { |
|
|
|
|
|
|
|
|
for (nodeId in this.nodes) { |
|
|
if (this.nodes.hasOwnProperty(nodeId)) { |
|
|
if (this.nodes.hasOwnProperty(nodeId)) { |
|
|
clusterLevel = this.nodes[nodeId].clusterSessions.length; |
|
|
clusterLevel = this.nodes[nodeId].clusterSessions.length; |
|
|
if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} |
|
|
if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} |
|
@ -14425,7 +14439,7 @@ var ClusterMixin = { |
|
|
var amountOfNodes = this.nodeIndices.length; |
|
|
var amountOfNodes = this.nodeIndices.length; |
|
|
var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; |
|
|
var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; |
|
|
// we loop over all nodes in the list
|
|
|
// we loop over all nodes in the list
|
|
|
for (var nodeId in this.nodes) { |
|
|
|
|
|
|
|
|
for (nodeId in this.nodes) { |
|
|
if (this.nodes.hasOwnProperty(nodeId)) { |
|
|
if (this.nodes.hasOwnProperty(nodeId)) { |
|
|
if (this.nodes[nodeId].clusterSessions.length < targetLevel) { |
|
|
if (this.nodes[nodeId].clusterSessions.length < targetLevel) { |
|
|
this._clusterToSmallestNeighbour(this.nodes[nodeId]); |
|
|
this._clusterToSmallestNeighbour(this.nodes[nodeId]); |
|
@ -14740,7 +14754,7 @@ var SelectionMixin = { |
|
|
} |
|
|
} |
|
|
for(var edgeId in this.selectionObj.edges) { |
|
|
for(var edgeId in this.selectionObj.edges) { |
|
|
if(this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
if(this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
this.selectionObj.edges[edgeId].unselect();; |
|
|
|
|
|
|
|
|
this.selectionObj.edges[edgeId].unselect(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -15410,15 +15424,15 @@ var graphMixinLoaders = { |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_loadSectorSystem: function () { |
|
|
_loadSectorSystem: function () { |
|
|
this.sectors = { }, |
|
|
|
|
|
this.activeSector = ["default"]; |
|
|
|
|
|
this.sectors["active"] = { }, |
|
|
|
|
|
this.sectors["active"]["default"] = {"nodes": {}, |
|
|
|
|
|
|
|
|
this.sectors = {}; |
|
|
|
|
|
this.activeSector = ["default"]; |
|
|
|
|
|
this.sectors["active"] = {}; |
|
|
|
|
|
this.sectors["active"]["default"] = {"nodes": {}, |
|
|
"edges": {}, |
|
|
"edges": {}, |
|
|
"nodeIndices": [], |
|
|
"nodeIndices": [], |
|
|
"formationScale": 1.0, |
|
|
"formationScale": 1.0, |
|
|
"drawingNode": undefined }; |
|
|
"drawingNode": undefined }; |
|
|
this.sectors["frozen"] = {}, |
|
|
|
|
|
|
|
|
this.sectors["frozen"] = {}; |
|
|
this.sectors["support"] = {"nodes": {}, |
|
|
this.sectors["support"] = {"nodes": {}, |
|
|
"edges": {}, |
|
|
"edges": {}, |
|
|
"nodeIndices": [], |
|
|
"nodeIndices": [], |
|
@ -15451,7 +15465,7 @@ var graphMixinLoaders = { |
|
|
_loadManipulationSystem: function () { |
|
|
_loadManipulationSystem: function () { |
|
|
// reset global variables -- these are used by the selection of nodes and edges.
|
|
|
// reset global variables -- these are used by the selection of nodes and edges.
|
|
|
this.blockConnectingEdgeSelection = false; |
|
|
this.blockConnectingEdgeSelection = false; |
|
|
this.forceAppendSelection = false |
|
|
|
|
|
|
|
|
this.forceAppendSelection = false; |
|
|
|
|
|
|
|
|
if (this.constants.dataManipulation.enabled == true) { |
|
|
if (this.constants.dataManipulation.enabled == true) { |
|
|
// load the manipulator HTML elements. All styling done in css.
|
|
|
// load the manipulator HTML elements. All styling done in css.
|
|
@ -15915,6 +15929,7 @@ Graph.prototype._centerGraph = function(range) { |
|
|
* This function zooms out to fit all data on screen based on amount of nodes |
|
|
* This function zooms out to fit all data on screen based on amount of nodes |
|
|
* |
|
|
* |
|
|
* @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; |
|
|
* @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; |
|
|
|
|
|
* @param {Boolean} [disableStart] | If true, start is not called. |
|
|
*/ |
|
|
*/ |
|
|
Graph.prototype.zoomExtent = function(initialZoom, disableStart) { |
|
|
Graph.prototype.zoomExtent = function(initialZoom, disableStart) { |
|
|
if (initialZoom === undefined) { |
|
|
if (initialZoom === undefined) { |
|
@ -16501,7 +16516,7 @@ Graph.prototype._handleOnDrag = function(event) { |
|
|
this.drag.translation.x + diffX, |
|
|
this.drag.translation.x + diffX, |
|
|
this.drag.translation.y + diffY); |
|
|
this.drag.translation.y + diffY); |
|
|
this._redraw(); |
|
|
this._redraw(); |
|
|
this.moved = true; |
|
|
|
|
|
|
|
|
this.moving = true; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -17355,7 +17370,12 @@ Graph.prototype._stabilize = function() { |
|
|
this.emit("stabilized",{iterations:count}); |
|
|
this.emit("stabilized",{iterations:count}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization |
|
|
|
|
|
* because only the supportnodes for the smoothCurves have to settle. |
|
|
|
|
|
* |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._freezeDefinedNodes = function() { |
|
|
Graph.prototype._freezeDefinedNodes = function() { |
|
|
var nodes = this.nodes; |
|
|
var nodes = this.nodes; |
|
|
for (var id in nodes) { |
|
|
for (var id in nodes) { |
|
@ -17370,6 +17390,11 @@ Graph.prototype._freezeDefinedNodes = function() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Unfreezes the nodes that have been frozen by _freezeDefinedNodes. |
|
|
|
|
|
* |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._restoreFrozenNodes = function() { |
|
|
Graph.prototype._restoreFrozenNodes = function() { |
|
|
var nodes = this.nodes; |
|
|
var nodes = this.nodes; |
|
|
for (var id in nodes) { |
|
|
for (var id in nodes) { |
|
@ -17440,7 +17465,11 @@ Graph.prototype._discreteStepNodes = function() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* A single simulation step (or "tick") in the physics simulation |
|
|
|
|
|
* |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._physicsTick = function() { |
|
|
Graph.prototype._physicsTick = function() { |
|
|
if (!this.freezeSimulation) { |
|
|
if (!this.freezeSimulation) { |
|
|
if (this.moving) { |
|
|
if (this.moving) { |
|
@ -17559,7 +17588,12 @@ Graph.prototype.toggleFreeze = function() { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* This function cleans the support nodes if they are not needed and adds them when they are. |
|
|
|
|
|
* |
|
|
|
|
|
* @param {boolean} [disableStart] |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._configureSmoothCurves = function(disableStart) { |
|
|
Graph.prototype._configureSmoothCurves = function(disableStart) { |
|
|
if (disableStart === undefined) { |
|
|
if (disableStart === undefined) { |
|
|
disableStart = true; |
|
|
disableStart = true; |
|
@ -17585,6 +17619,13 @@ Graph.prototype._configureSmoothCurves = function(disableStart) { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but |
|
|
|
|
|
* are used for the force calculation. |
|
|
|
|
|
* |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._createBezierNodes = function() { |
|
|
Graph.prototype._createBezierNodes = function() { |
|
|
if (this.constants.smoothCurves == true) { |
|
|
if (this.constants.smoothCurves == true) { |
|
|
for (var edgeId in this.edges) { |
|
|
for (var edgeId in this.edges) { |
|
@ -17609,7 +17650,11 @@ Graph.prototype._createBezierNodes = function() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* load the functions that load the mixins into the prototype. |
|
|
|
|
|
* |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
Graph.prototype._initializeMixinLoaders = function () { |
|
|
Graph.prototype._initializeMixinLoaders = function () { |
|
|
for (var mixinFunction in graphMixinLoaders) { |
|
|
for (var mixinFunction in graphMixinLoaders) { |
|
|
if (graphMixinLoaders.hasOwnProperty(mixinFunction)) { |
|
|
if (graphMixinLoaders.hasOwnProperty(mixinFunction)) { |
|
|