Browse Source

Merge remote-tracking branch 'origin/develop' into develop

Conflicts:
	examples/timeline/03_much_data.html
css_transitions
jos 10 years ago
parent
commit
a89905017e
16 changed files with 342 additions and 267 deletions
  1. +1
    -1
      HISTORY.md
  2. +164
    -119
      dist/vis.js
  3. +10
    -10
      dist/vis.min.js
  4. +3
    -18
      examples/timeline/01_basic.html
  5. +3
    -3
      examples/timeline/03_much_data.html
  6. +1
    -1
      src/graph/Edge.js
  7. +36
    -5
      src/graph/Graph.js
  8. +1
    -0
      src/graph/Node.js
  9. +4
    -2
      src/graph/graphMixins/ClusterMixin.js
  10. +3
    -3
      src/graph/graphMixins/HierarchicalLayoutMixin.js
  11. +0
    -2
      src/graph/graphMixins/ManipulationMixin.js
  12. +6
    -6
      src/graph/graphMixins/MixinLoader.js
  13. +1
    -1
      src/graph/graphMixins/SelectionMixin.js
  14. +23
    -0
      src/graph/graphMixins/physics/BarnesHut.js
  15. +29
    -1
      src/graph/graphMixins/physics/PhysicsMixin.js
  16. +57
    -95
      src/timeline/component/ItemSet.js

+ 1
- 1
HISTORY.md View File

@ -7,7 +7,7 @@ http://visjs.org
### Timeline
- Large refactoring of the Timeline, simplifying the code.
- Performance improvements.
- Great performance improvements.
- Improved layout of box-items inside groups.
- Function `setWindow` now accepts an object with properties `start` and `end`.
- Fixed option `autoResize` forcing a repaint of the Timeline with every check

+ 164
- 119
dist/vis.js View File

@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
* @version @@version
* @date @@date
* @version 0.7.5-SNAPSHOT
* @date 2014-04-29
*
* @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
@ -4772,7 +4772,7 @@ function ItemSet(backgroundPanel, axisPanel, options) {
byStart: [],
byEnd: []
};
this.systemLoaded = false;
// this.systemLoaded = false;
this.visibleItems = []; // visible, ordered items
this.selection = []; // list with the ids of all selected nodes
this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
@ -4999,7 +4999,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
if (high == 0) {guess = -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;
}
else {
@ -5009,7 +5009,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
else {
high -= 1;
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;
}
else {
@ -5034,6 +5034,48 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
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
* @return {boolean} Returns true if the component is resized
@ -5056,30 +5098,20 @@ ItemSet.prototype.repaint = function repaint() {
var newVisibleItems = [];
var item;
var range = this.range;
var orderedItems = this.orderedItems;
// 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.
if (this.visibleItems.length > 0) {
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 (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)
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 (initialPosByStart != -1) {
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++) {
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 (initialPosByEnd != -1) {
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++) {
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
//this.stack.order(this.visibleItems); // TODO: improve ordering
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;
if (type == 'range') {
me._checkIfVisible(item,this.visibleItems);
}
});
this._order();
this.systemLoaded = false;
// this.systemLoaded = false;
this.stackDirty = true; // force re-stacking of all items next repaint
this.emit('change');
};
@ -7816,7 +7778,7 @@ Timeline.prototype._onAddItem = function (event) {
}
else {
// 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 newItem = {
start: this.timeAxis.snap(this._toTime(x)),
@ -9312,6 +9274,7 @@ Node.prototype.discreteStep = function(interval) {
/**
* Perform one discrete step for the node
* @param {number} interval Time interval in seconds
* @param {number} maxVelocity The speed limit imposed on the velocity
*/
Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
if (!this.xFixed) {
@ -10019,7 +9982,7 @@ Edge.prototype.setProperties = function(properties, constants) {
this.customLength = true;}
// scale the arrow
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor};
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;}
// Added to support dashed lines
// David Jordan
@ -11419,6 +11382,13 @@ var physicsMixin = {
}
},
/**
* This overwrites the this.constants.
*
* @param constantsVariableName
* @param value
* @private
*/
_overWriteGraphConstants: function (constantsVariableName, value) {
var nameArray = constantsVariableName.split("_");
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 () {
this.constants.smoothCurves = !this.constants.smoothCurves;
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
@ -11442,6 +11415,10 @@ function graphToggleSmoothCurves () {
this._configureSmoothCurves(false);
};
/**
* this function is used to scramble the nodes
*
*/
function graphRepositionNodes () {
for (var nodeId in this.calculationNodes) {
if (this.calculationNodes.hasOwnProperty(nodeId)) {
@ -11459,6 +11436,9 @@ function graphRepositionNodes () {
this.start();
};
/**
* this is used to generate an options file from the playing with physics system.
*/
function graphGenerateOptions () {
var options = "No options are required, default values used.";
var optionsSpecific = [];
@ -11556,7 +11536,10 @@ function graphGenerateOptions () {
};
/**
* this is used to switch between barnesHut, repulsion and hierarchical.
*
*/
function switchConfigurations () {
var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"];
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) {
var valueId = 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) {
var totalMass = parentBranch.mass + node.mass;
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) {
if (skipMassUpdate != true || skipMassUpdate === undefined) {
// 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) {
switch (parentBranch.children[region].childrenCount) {
case 0: // place node here
@ -12249,7 +12263,7 @@ var HierarchicalLayoutMixin = {
*/
_getDistribution : function() {
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.
// 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
var maxCount = 0;
for (var level in distribution) {
for (level in distribution) {
if (distribution.hasOwnProperty(level)) {
if (maxCount < distribution[level].amount) {
maxCount = distribution[level].amount;
@ -12283,7 +12297,7 @@ var HierarchicalLayoutMixin = {
}
// set the initial position and spacing of each nodes accordingly
for (var level in distribution) {
for (level in distribution) {
if (distribution.hasOwnProperty(level)) {
distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing;
distribution[level].nodeSpacing /= (distribution[level].amount + 1);
@ -12725,8 +12739,6 @@ var manipulationMixin = {
/**
* Adds a node on the specified location
*
* @param {Object} pointer
*/
_addNode : function() {
if (this._selectionIsEmpty() && this.editMode == true) {
@ -13561,6 +13573,7 @@ var ClusterMixin = {
* @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} force | enabled or disable forcing
* @param {Boolean} doNotStart | if true do not call start
*
*/
updateClusters : function(zoomDirection,recursive,force,doNotStart) {
@ -14411,9 +14424,10 @@ var ClusterMixin = {
var maxLevel = 0;
var minLevel = 1e9;
var clusterLevel = 0;
var nodeId;
// we loop over all nodes in the list
for (var nodeId in this.nodes) {
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
clusterLevel = this.nodes[nodeId].clusterSessions.length;
if (maxLevel < clusterLevel) {maxLevel = clusterLevel;}
@ -14425,7 +14439,7 @@ var ClusterMixin = {
var amountOfNodes = this.nodeIndices.length;
var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference;
// 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[nodeId].clusterSessions.length < targetLevel) {
this._clusterToSmallestNeighbour(this.nodes[nodeId]);
@ -14740,7 +14754,7 @@ var SelectionMixin = {
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
this.selectionObj.edges[edgeId].unselect();;
this.selectionObj.edges[edgeId].unselect();
}
}
@ -15410,15 +15424,15 @@ var graphMixinLoaders = {
* @private
*/
_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": {},
"nodeIndices": [],
"formationScale": 1.0,
"drawingNode": undefined };
this.sectors["frozen"] = {},
this.sectors["frozen"] = {};
this.sectors["support"] = {"nodes": {},
"edges": {},
"nodeIndices": [],
@ -15451,7 +15465,7 @@ var graphMixinLoaders = {
_loadManipulationSystem: function () {
// reset global variables -- these are used by the selection of nodes and edges.
this.blockConnectingEdgeSelection = false;
this.forceAppendSelection = false
this.forceAppendSelection = false;
if (this.constants.dataManipulation.enabled == true) {
// 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
*
* @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) {
if (initialZoom === undefined) {
@ -16501,7 +16516,7 @@ Graph.prototype._handleOnDrag = function(event) {
this.drag.translation.x + diffX,
this.drag.translation.y + diffY);
this._redraw();
this.moved = true;
this.moving = true;
}
};
@ -17355,7 +17370,12 @@ Graph.prototype._stabilize = function() {
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() {
var nodes = this.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() {
var nodes = this.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() {
if (!this.freezeSimulation) {
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) {
if (disableStart === undefined) {
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() {
if (this.constants.smoothCurves == true) {
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 () {
for (var mixinFunction in graphMixinLoaders) {
if (graphMixinLoaders.hasOwnProperty(mixinFunction)) {

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


+ 3
- 18
examples/timeline/01_basic.html View File

@ -21,26 +21,11 @@
{id: 1, content: 'item 1', start: '2014-04-20'},
{id: 2, content: 'item 2', start: '2014-04-14'},
{id: 3, content: 'item 3', start: '2014-04-18'},
{id: 4, content: 'item 4', start: '2014-04-16', end: '2014-04-19'},
{id: 5, content: 'item 5', start: '2014-04-25'},
{id: 6, content: 'item 6', start: '2014-04-27'},
{id: 7, content: 'item 3', start: '2014-06-18'},
{id: 8, content: 'item 5', start: '2014-06-25'},
{id: 9, content: 'item 6', start: '2014-06-27'},
{id: 10, content: 'item 3', start: '2014-06-18'},
{id: 11, content: 'item 5', start: '2014-06-25'},
{id: 12, content: 'item 6', start: '2014-01-27'},
{id: 13, content: 'item 3', start: '2014-01-18'},
{id: 14, content: 'item 5', start: '2014-01-25'},
{id: 15, content: 'item 6', start: '2014-02-27'},
{id: 16, content: 'item 3', start: '2014-02-18'},
{id: 17, content: 'item 5', start: '2014-02-25'},
{id: 18, content: 'item 6', start: '2014-09-27'},
{id: 24, content: 'item 4', start: '2014-05-16', end: '2015-06-19'}
{id: 6, content: 'item 6', start: '2014-04-27'}
];
var options = {
start: '2014-02-10',
end: '2014-05-10'
};
var options = {};
var timeline = new vis.Timeline(container, items, options);
</script>
</body>

+ 3
- 3
examples/timeline/03_much_data.html View File

@ -21,9 +21,9 @@
Test with a lot of data
</h1>
<p>
<label for="count">Number of items</label>
<input id="count" value="1000">
<input id="draw" type="button" value="draw">
<label for="count">Number of items</label>
<input id="count" value="10000">
<input id="draw" type="button" value="draw">
</p>
<div id="visualization"></div>

+ 1
- 1
src/graph/Edge.js View File

@ -97,7 +97,7 @@ Edge.prototype.setProperties = function(properties, constants) {
this.customLength = true;}
// scale the arrow
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor};
if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;}
// Added to support dashed lines
// David Jordan

+ 36
- 5
src/graph/Graph.js View File

@ -372,6 +372,7 @@ Graph.prototype._centerGraph = function(range) {
* 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} [disableStart] | If true, start is not called.
*/
Graph.prototype.zoomExtent = function(initialZoom, disableStart) {
if (initialZoom === undefined) {
@ -958,7 +959,7 @@ Graph.prototype._handleOnDrag = function(event) {
this.drag.translation.x + diffX,
this.drag.translation.y + diffY);
this._redraw();
this.moved = true;
this.moving = true;
}
};
@ -1812,7 +1813,12 @@ Graph.prototype._stabilize = function() {
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() {
var nodes = this.nodes;
for (var id in nodes) {
@ -1827,6 +1833,11 @@ Graph.prototype._freezeDefinedNodes = function() {
}
};
/**
* Unfreezes the nodes that have been frozen by _freezeDefinedNodes.
*
* @private
*/
Graph.prototype._restoreFrozenNodes = function() {
var nodes = this.nodes;
for (var id in nodes) {
@ -1897,7 +1908,11 @@ Graph.prototype._discreteStepNodes = function() {
}
};
/**
* A single simulation step (or "tick") in the physics simulation
*
* @private
*/
Graph.prototype._physicsTick = function() {
if (!this.freezeSimulation) {
if (this.moving) {
@ -2016,7 +2031,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) {
if (disableStart === undefined) {
disableStart = true;
@ -2042,6 +2062,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() {
if (this.constants.smoothCurves == true) {
for (var edgeId in this.edges) {
@ -2066,7 +2093,11 @@ Graph.prototype._createBezierNodes = function() {
}
};
/**
* load the functions that load the mixins into the prototype.
*
* @private
*/
Graph.prototype._initializeMixinLoaders = function () {
for (var mixinFunction in graphMixinLoaders) {
if (graphMixinLoaders.hasOwnProperty(mixinFunction)) {

+ 1
- 0
src/graph/Node.js View File

@ -356,6 +356,7 @@ Node.prototype.discreteStep = function(interval) {
/**
* Perform one discrete step for the node
* @param {number} interval Time interval in seconds
* @param {number} maxVelocity The speed limit imposed on the velocity
*/
Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
if (!this.xFixed) {

+ 4
- 2
src/graph/graphMixins/ClusterMixin.js View File

@ -137,6 +137,7 @@ var ClusterMixin = {
* @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} force | enabled or disable forcing
* @param {Boolean} doNotStart | if true do not call start
*
*/
updateClusters : function(zoomDirection,recursive,force,doNotStart) {
@ -987,9 +988,10 @@ var ClusterMixin = {
var maxLevel = 0;
var minLevel = 1e9;
var clusterLevel = 0;
var nodeId;
// we loop over all nodes in the list
for (var nodeId in this.nodes) {
for (nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
clusterLevel = this.nodes[nodeId].clusterSessions.length;
if (maxLevel < clusterLevel) {maxLevel = clusterLevel;}
@ -1001,7 +1003,7 @@ var ClusterMixin = {
var amountOfNodes = this.nodeIndices.length;
var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference;
// 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[nodeId].clusterSessions.length < targetLevel) {
this._clusterToSmallestNeighbour(this.nodes[nodeId]);

+ 3
- 3
src/graph/graphMixins/HierarchicalLayoutMixin.js View File

@ -123,7 +123,7 @@ var HierarchicalLayoutMixin = {
*/
_getDistribution : function() {
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.
// the fix of X is removed after the x value has been set.
@ -148,7 +148,7 @@ var HierarchicalLayoutMixin = {
// determine the largest amount of nodes of all levels
var maxCount = 0;
for (var level in distribution) {
for (level in distribution) {
if (distribution.hasOwnProperty(level)) {
if (maxCount < distribution[level].amount) {
maxCount = distribution[level].amount;
@ -157,7 +157,7 @@ var HierarchicalLayoutMixin = {
}
// set the initial position and spacing of each nodes accordingly
for (var level in distribution) {
for (level in distribution) {
if (distribution.hasOwnProperty(level)) {
distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing;
distribution[level].nodeSpacing /= (distribution[level].amount + 1);

+ 0
- 2
src/graph/graphMixins/ManipulationMixin.js View File

@ -288,8 +288,6 @@ var manipulationMixin = {
/**
* Adds a node on the specified location
*
* @param {Object} pointer
*/
_addNode : function() {
if (this._selectionIsEmpty() && this.editMode == true) {

+ 6
- 6
src/graph/graphMixins/MixinLoader.js View File

@ -67,15 +67,15 @@ var graphMixinLoaders = {
* @private
*/
_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": {},
"nodeIndices": [],
"formationScale": 1.0,
"drawingNode": undefined };
this.sectors["frozen"] = {},
this.sectors["frozen"] = {};
this.sectors["support"] = {"nodes": {},
"edges": {},
"nodeIndices": [],
@ -108,7 +108,7 @@ var graphMixinLoaders = {
_loadManipulationSystem: function () {
// reset global variables -- these are used by the selection of nodes and edges.
this.blockConnectingEdgeSelection = false;
this.forceAppendSelection = false
this.forceAppendSelection = false;
if (this.constants.dataManipulation.enabled == true) {
// load the manipulator HTML elements. All styling done in css.

+ 1
- 1
src/graph/graphMixins/SelectionMixin.js View File

@ -174,7 +174,7 @@ var SelectionMixin = {
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
this.selectionObj.edges[edgeId].unselect();;
this.selectionObj.edges[edgeId].unselect();
}
}

+ 23
- 0
src/graph/graphMixins/physics/BarnesHut.js View File

@ -156,6 +156,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) {
var totalMass = parentBranch.mass + node.mass;
var totalMassInv = 1/totalMass;
@ -173,6 +180,14 @@ var barnesHutMixin = {
},
/**
* determine in which branch the node will be placed.
*
* @param parentBranch
* @param node
* @param skipMassUpdate
* @private
*/
_placeInTree : function(parentBranch,node,skipMassUpdate) {
if (skipMassUpdate != true || skipMassUpdate === undefined) {
// update the mass of the branch.
@ -198,6 +213,14 @@ var barnesHutMixin = {
},
/**
* actually place the node in a region (or branch)
*
* @param parentBranch
* @param node
* @param region
* @private
*/
_placeInRegion : function(parentBranch,node,region) {
switch (parentBranch.children[region].childrenCount) {
case 0: // place node here

+ 29
- 1
src/graph/graphMixins/physics/PhysicsMixin.js View File

@ -464,6 +464,13 @@ var physicsMixin = {
}
},
/**
* This overwrites the this.constants.
*
* @param constantsVariableName
* @param value
* @private
*/
_overWriteGraphConstants: function (constantsVariableName, value) {
var nameArray = constantsVariableName.split("_");
if (nameArray.length == 1) {
@ -478,6 +485,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 () {
this.constants.smoothCurves = !this.constants.smoothCurves;
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
@ -487,6 +497,10 @@ function graphToggleSmoothCurves () {
this._configureSmoothCurves(false);
};
/**
* this function is used to scramble the nodes
*
*/
function graphRepositionNodes () {
for (var nodeId in this.calculationNodes) {
if (this.calculationNodes.hasOwnProperty(nodeId)) {
@ -504,6 +518,9 @@ function graphRepositionNodes () {
this.start();
};
/**
* this is used to generate an options file from the playing with physics system.
*/
function graphGenerateOptions () {
var options = "No options are required, default values used.";
var optionsSpecific = [];
@ -601,7 +618,10 @@ function graphGenerateOptions () {
};
/**
* this is used to switch between barnesHut, repulsion and hierarchical.
*
*/
function switchConfigurations () {
var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"];
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value;
@ -640,6 +660,14 @@ function switchConfigurations () {
}
/**
* this generates the ranges depending on the iniital values.
*
* @param id
* @param map
* @param constantsVariableName
*/
function showValueOfRange (id,map,constantsVariableName) {
var valueId = id + "_value";
var rangeValue = document.getElementById(id).value;

+ 57
- 95
src/timeline/component/ItemSet.js View File

@ -43,7 +43,7 @@ function ItemSet(backgroundPanel, axisPanel, options) {
byStart: [],
byEnd: []
};
this.systemLoaded = false;
// this.systemLoaded = false;
this.visibleItems = []; // visible, ordered items
this.selection = []; // list with the ids of all selected nodes
this.queue = {}; // queue with id/actions: 'add', 'update', 'delete'
@ -270,7 +270,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
if (high == 0) {guess = -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;
}
else {
@ -280,7 +280,7 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
else {
high -= 1;
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;
}
else {
@ -305,6 +305,48 @@ ItemSet.prototype._binarySearch = function _binarySearch(byEnd) {
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
* @return {boolean} Returns true if the component is resized
@ -327,30 +369,20 @@ ItemSet.prototype.repaint = function repaint() {
var newVisibleItems = [];
var item;
var range = this.range;
var orderedItems = this.orderedItems;
// 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.
if (this.visibleItems.length > 0) {
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 (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)
var initialPosByEnd = this._binarySearch(true);
@ -358,96 +390,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 (initialPosByStart != -1) {
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++) {
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 (initialPosByEnd != -1) {
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++) {
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
//this.stack.order(this.visibleItems); // TODO: improve ordering
var force = this.stackDirty || zoomed; // force re-stacking of all items if true
@ -643,10 +602,13 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
}
me.items[id] = item;
if (type == 'range') {
me._checkIfVisible(item,this.visibleItems);
}
});
this._order();
this.systemLoaded = false;
// this.systemLoaded = false;
this.stackDirty = true; // force re-stacking of all items next repaint
this.emit('change');
};

Loading…
Cancel
Save