Browse Source

Merge branch 'develop' into simplified

css_transitions
josdejong 10 years ago
parent
commit
4c085ef2ee
24 changed files with 1201 additions and 417 deletions
  1. +22
    -0
      HISTORY.md
  2. +1
    -1
      bower.json
  3. +18
    -14
      dist/vis.css
  4. +488
    -167
      dist/vis.js
  5. +1
    -1
      dist/vis.min.css
  6. +11
    -10
      dist/vis.min.js
  7. +147
    -38
      docs/graph.html
  8. +2
    -1
      examples/graph/20_navigation.html
  9. +1
    -1
      package.json
  10. +39
    -20
      src/graph/Edge.js
  11. +131
    -46
      src/graph/Graph.js
  12. +12
    -7
      src/graph/Node.js
  13. +18
    -14
      src/graph/css/graph-navigation.css
  14. +3
    -3
      src/graph/graphMixins/ClusterMixin.js
  15. +4
    -1
      src/graph/graphMixins/HierarchicalLayoutMixin.js
  16. +10
    -8
      src/graph/graphMixins/ManipulationMixin.js
  17. +1
    -1
      src/graph/graphMixins/MixinLoader.js
  18. +33
    -1
      src/graph/graphMixins/NavigationMixin.js
  19. +72
    -60
      src/graph/graphMixins/SelectionMixin.js
  20. +1
    -1
      src/graph/graphMixins/physics/BarnesHut.js
  21. +170
    -21
      src/graph/graphMixins/physics/PhysicsMixin.js
  22. +1
    -1
      src/timeline/component/css/groupset.css
  23. +1
    -0
      src/timeline/component/css/item.css
  24. +14
    -0
      src/util.js

+ 22
- 0
HISTORY.md View File

@ -1,6 +1,28 @@
vis.js history
http://visjs.org
## 2014-03-27, version 0.7.1
### Graph
- fixed edge color bug.
- fixed select event bug.
- clarified docs, stressing importance of css inclusion for correct display of navigation an manipulation icons.
- improved and expanded playing with physics (configurePhysics option).
- added highlights to navigation icons if the corresponding key is pressed.
- added freezeForStabilization option to improve stabilization with cached positions.
## 2014-03-07, version 0.7.0
### Graph
- changed navigation CSS. Icons are now always correctly positioned.
- added stabilizationIterations option to graph.
- added storePosition() method to save the XY positions of nodes in the DataSet.
- separated allowedToMove into allowedToMoveX and allowedToMoveY. This is required for initializing nodes from hierarchical layouts after storePosition().
- added color options for the edges.
## 2014-03-06, version 0.6.1

+ 1
- 1
bower.json View File

@ -1,6 +1,6 @@
{
"name": "vis",
"version": "0.7.0-SNAPSHOT",
"version": "0.7.2-SNAPSHOT",
"description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/",
"repository": {

+ 18
- 14
dist/vis.css View File

@ -427,38 +427,42 @@ div.graph-navigation:active {
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
}
div.graph-navigation.active {
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
}
div.graph-navigation.up {
background-image: url("img/graph/upArrow.png");
margin-top:520px;
margin-left:55px;
bottom:50px;
left:55px;
}
div.graph-navigation.down {
background-image: url("img/graph/downArrow.png");
margin-top:560px;
margin-left:55px;
bottom:10px;
left:55px;
}
div.graph-navigation.left {
background-image: url("img/graph/leftArrow.png");
margin-top:560px;
margin-left:15px;
bottom:10px;
left:15px;
}
div.graph-navigation.right {
background-image: url("img/graph/rightArrow.png");
margin-top:560px;
margin-left:95px;
bottom:10px;
left:95px;
}
div.graph-navigation.zoomIn {
background-image: url("img/graph/plus.png");
margin-top:560px;
margin-left:555px;
bottom:10px;
right:15px;
}
div.graph-navigation.zoomOut {
background-image: url("img/graph/minus.png");
margin-top:560px;
margin-left:515px;
bottom:10px;
right:55px;
}
div.graph-navigation.zoomExtends {
background-image: url("img/graph/zoomExtends.png");
margin-top:520px;
margin-left:555px;
bottom:50px;
right:15px;
}

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


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


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


+ 147
- 38
docs/graph.html View File

@ -25,7 +25,13 @@
<p>
The graph visualization works smooth on any modern browser for up to a
few thousand nodes and edges. To handle a larger amount of nodes, Graph
has clustering support.
has <a href="#Clustering">clustering</a> support.
</p>
<p>
Every dataset is different. Nodes can have different sizes based on content, interconnectivity can be high or low etc. Because of this, graph has a special option
that the user can use to explore which settings may be good for you. Use configurePhysics as described in the <u><a href="#PhysicsConfiguration">Physics</a></u> section or by
<u><a href="../examples/graph/25_physics_configuration.html">example 25</a></u>.
</p>
<p>
@ -292,15 +298,22 @@ var nodes = [
the same color schema.</td>
</tr>
<tr>
<td>allowedToMove</td>
<td>Boolean</td>
<td>true</td>
<td>If allowedToMove is false, then the node will not move from its supplied position.
If only an x position has been supplied, it is only fixed in the x-direction.
The same holds for y. If both x and y have been defined, the node will not move.
If no x or y have been supplied, this argument will not do anything.</td>
</tr>
<tr>
<td>allowedToMoveX</td>
<td>Boolean</td>
<td>false</td>
<td>If allowedToMoveX is false, then the node will not move from its supplied position.
If an X position has been supplied, it is fixed in the X-direction.
If no X value has been supplied, this argument will not do anything.</td>
</tr>
<tr>
<td>allowedToMoveY</td>
<td>Boolean</td>
<td>false</td>
<td>If allowedToMoveY is false, then the node will not move from its supplied position.
If an Y position has been supplied, it is fixed in the Y-direction.
If no Y value has been supplied, this argument will not do anything.</td>
</tr>
<tr>
<td>fontColor</td>
<td>String</td>
@ -339,7 +352,13 @@ var nodes = [
<td>Url of an image. Only applicable when the shape of the node is
<code>image</code>.</td>
</tr>
<tr>
<td>mass</td>
<td>number</td>
<td>1</td>
<td>When using the Barnes Hut simulation method (which is selected by default),
the mass of a node determines the gravitational repulsion during the simulation. Higher mass will push other nodes further away.</td>
</tr>
<tr>
<td>level</td>
<td>number</td>
@ -469,12 +488,26 @@ var edges = [
<th>Description</th>
</tr>
<tr>
<td>color</td>
<td>string</td>
<td>no</td>
<td>A HTML color for the edge.</td>
</tr>
<tr>
<td>color</td>
<td>String | Object</td>
<td>no</td>
<td>Color for the edge.</td>
</tr>
<tr>
<td>color.color</td>
<td>String</td>
<td>no</td>
<td>Color of the edge when not selected.</td>
</tr>
<tr>
<td>color.highlight</td>
<td>String</td>
<td>no</td>
<td>Color of the edge when selected.</td>
</tr>
<tr>
<td>dash</td>
@ -565,7 +598,12 @@ var edges = [
<td>no</td>
<td>Text label to be displayed halfway the edge.</td>
</tr>
<tr>
<td>length</td>
<td>number</td>
<td>physics.[method].springLength</td>
<td>The resting length of the edge when modeled as a spring. By default the springLength determined by the physics is used. By using this setting you can make certain edges have different resting lengths.</td>
</tr>
<tr>
<td>title</td>
<td>string</td>
@ -706,6 +744,17 @@ var options = {
</td>
</tr>
<tr>
<td>freezeForStabilization</a></td>
<td>Boolean</td>
<td>false</td>
<td>
With the advent of the storePosition() function, the positions of the nodes can be saved after they are stabilized. The smoothCurves require support nodes and those positions are not stored. In order
to speed up the initialization of the graph by using storePosition() and loading the nodes with the stored positions, the freezeForStabilization option freezes all nodes that have been supplied with
an x and y position in place during the stabilization. That way only the support nodes for the smooth curves have to stabilize, greatly speeding up the stabilization process with cached positions.
</td>
</tr>
<tr>
<td><a href="#Groups_configuration">groups</a></td>
<td>Object</td>
@ -774,6 +823,14 @@ var options = {
the nodes move to a stabe position visibly in an animated way.</td>
</tr>
<tr>
<td>stabilizationIterations</td>
<td>Number</td>
<td>1000</td>
<td>If stabilize is set to true, this number is the (maximum) amount of physics steps the stabilization process takes
before showing the result. If your simulation takes too long to stabilize, this number can be reduced. On the other hand, if your graph is not stabilized after loading, this number can be increased.</td>
</tr>
<tr>
<td>width</td>
<td>String</td>
@ -855,13 +912,20 @@ var options = {
<td>Default border color of the node when selected.</td>
</tr>
<tr>
<td>allowedToMove</td>
<td>allowedToMoveX</td>
<td>Boolean</td>
<td>false</td>
<td>If allowedToMoveX is false, then the node will not move from its supplied position.
If an X position has been supplied, it is fixed in the X-direction.
If no X value has been supplied, this argument will not do anything.</td>
</tr>
<tr>
<td>allowedToMoveY</td>
<td>Boolean</td>
<td>false</td>
<td>If allowedToMove is false, then the node will not move from its supplied position.
If only an x position has been supplied, it is only fixed in the x-direction.
The same holds for y. If both x and y have been defined, the node will not move.
If no x or y have been supplied, this argument will not do anything.</td>
<td>If allowedToMoveY is false, then the node will not move from its supplied position.
If an Y position has been supplied, it is fixed in the Y-direction.
If no Y value has been supplied, this argument will not do anything.</td>
</tr>
<tr>
@ -894,7 +958,13 @@ var options = {
<td>none</td>
<td>Default image url for the nodes. only applicable to shape <code>image</code>.</td>
</tr>
<tr>
<td>mass</td>
<td>number</td>
<td>1</td>
<td>When using the Barnes Hut simulation method (which is selected by default),
the mass of a node determines the gravitational repulsion during the simulation. Higher mass will push other nodes further away.</td>
</tr>
<tr>
<td>level</td>
<td>number</td>
@ -981,12 +1051,26 @@ var options = {
<th>Description</th>
</tr>
<tr>
<td>color</td>
<td>String</td>
<td>"#2B7CE9"</td>
<td>The default color of a edge.</td>
</tr>
<tr>
<td>color</td>
<td>String | Object</td>
<td>Object</td>
<td>Colors of the edge. This object contains both colors for the selected and unselected state.</td>
</tr>
<tr>
<td>color.color</td>
<td>String</td>
<td>"#848484"</td>
<td>Color of the edge when not selected.</td>
</tr>
<tr>
<td>color.highlight</td>
<td>String</td>
<td>"#848484"</td>
<td>Color of the edge when selected.</td>
</tr>
<tr>
<td>dash</td>
@ -1026,6 +1110,12 @@ var options = {
<td>Default length of a gap in pixels on a dashed line.
Only applicable when the line style is <code>dash-line</code>.</td>
</tr>
<tr>
<td>length</td>
<td>number</td>
<td>physics.[method].springLength</td>
<td>The resting length of the edge when modeled as a spring. By default the springLength determined by the physics is used. By using this setting you can make certain edges have different resting lengths.</td>
</tr>
<tr>
<td>style</td>
@ -1197,8 +1287,8 @@ var options = {
enabled: true,
gravitationalConstant: -2000,
centralGravity: 0.1,
springLength: 100,
springConstant: 0.05,
springLength: 95,
springConstant: 0.04,
damping: 0.09
},
repulsion: {
@ -1239,14 +1329,14 @@ var options = {
<tr>
<td>springLength</td>
<td>Number</td>
<td>100</td>
<td>95</td>
<td>In the previous versions this was a property of the edges, called length. This is the length of the springs when they are at rest. During the simulation they will be streched by the gravitational fields.
To greatly reduce the edge length, the gravitationalConstant has to be reduced as well.</td>
</tr>
<tr>
<td>springConstant</td>
<td>Number</td>
<td>0.05</td>
<td>0.04</td>
<td>This is the spring constant used to calculate the spring forces based on Hooke&prime;s Law. More information is available <a href="http://en.wikipedia.org/wiki/Hooke's_law" target="_blank">here</a>.</td>
</tr>
<tr>
@ -1314,7 +1404,8 @@ var options = {
By using the data manipulation feature of the graph you can dynamically create nodes, connect nodes with edges, edit nodes or delete nodes and edges.
The toolbar is fully HTML and CSS so the user can style this to their preference. To control the behaviour of the data manipulation, users can insert custom functions
into the data manipulation process. For example, an injected function can show an detailed pop-up when a user wants to add a node. In <a href="../examples/graph/21_data_manipulation.html">example 21</a>,
two functions have been injected into the add and edit functionality. This is described in more detail in the next subsection.
two functions have been injected into the add and edit functionality. This is described in more detail in the next subsection. To correctly display the manipulation icons, the <b>vis.css</b> file must be included.
The user is free to alter or overload the CSS classes but without them the navigation icons are not visible.
</p>
<pre class="prettyprint">
// These variables must be defined in an options object named dataManipulation.
@ -1371,7 +1462,8 @@ var options: {
* label: new,
* x: x position of click (canvas space),
* y: y position of click (canvas space),
* allowedToMove: true
* allowedToMoveX: true,
* allowedToMoveY: true
* };
*/
var newData = {..}; // alter the data as you want.
@ -1607,7 +1699,8 @@ var options: {
<h3 id="Navigation_controls">Navigation controls</h3>
<p>
Graph has a menu with navigation controls, which is disabled by default.
It can be configured with the following settings.
It can be configured with the following settings. To correctly display the navigation icons, the <b>vis.css</b> file must be included.
The user is free to alter or overload the CSS classes but without them the navigation icons are not visible.
</p>
<pre class="prettyprint">
@ -1760,6 +1853,13 @@ var options: {
The selections are not ordered.
</td>
</tr>
<tr>
<td>storePosition()</td>
<td>none</td>
<td>This will put the X and Y positions of all nodes in the dataset. It will also include allowedToMoveX and allowedToMoveY with the correct values.
You can use this to stablize your graph once, then save the positions in a database so the next time you load the nodes, stabilization will be near instantaneous.
</td>
</tr>
<tr>
<td>on(event, callback)</td>
@ -1818,7 +1918,7 @@ var options: {
</tr>
<tr>
<td>zoomExtent</td>
<td>zoomExtent()</td>
<td>none</td>
<td>Scales the graph so all the nodes are in center view.</td>
</tr>
@ -1913,6 +2013,15 @@ graph.off('select', onSelect);
</ul>
</td>
</tr>
<tr>
<td>stabilized</td>
<td>Fired when the graph has been stabilized after initialization. This event can be used to trigger the .storePosition() function after stabilization.</td>
<td>
<ul>
<li><code>iterations</code>: number of iterations used to stabilize</li>
</ul>
</td>
</tr>
</table>

+ 2
- 1
examples/graph/20_navigation.html View File

@ -161,7 +161,8 @@
</table>
<br />
Apart from clicking the icons, you can also navigate using the keyboard. The buttons are in table above.
Zoom Extends changes the zoom and position of the camera to encompass all visible nodes.
Zoom Extends changes the zoom and position of the camera to encompass all visible nodes. <u>To correctly display the navigation icons, the <b>vis.css</b> file must be included.</u>
The user is free to alter or overload the CSS classes but without them the navigation icons are not visible.
</div>

+ 1
- 1
package.json View File

@ -1,6 +1,6 @@
{
"name": "vis",
"version": "0.7.0-SNAPSHOT",
"version": "0.7.2-SNAPSHOT",
"description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/",
"repository": {

+ 39
- 20
src/graph/Edge.js View File

@ -52,7 +52,8 @@ function Edge (properties, graph, constants) {
// 2012-08-08
this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
this.color = constants.edges.color;
this.color = {color:constants.edges.color.color,
highlight:constants.edges.color.highlight};
this.widthFixed = false;
this.lengthFixed = false;
@ -80,6 +81,7 @@ Edge.prototype.setProperties = function(properties, constants) {
this.fontSize = constants.edges.fontSize;
this.fontFace = constants.edges.fontFace;
this.fontColor = constants.edges.fontColor;
if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;}
if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;}
if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;}
@ -100,7 +102,16 @@ Edge.prototype.setProperties = function(properties, constants) {
if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;}
}
if (properties.color !== undefined) {this.color = properties.color;}
if (properties.color !== undefined) {
if (util.isString(properties.color)) {
this.color.color = properties.color;
this.color.highlight = properties.color;
}
else {
if (properties.color.color !== undefined) {this.color.color = properties.color.color;}
if (properties.color.highlight !== undefined) {this.color.highlight = properties.color.highlight;}
}
}
// A node is connected when it has a from and to node.
this.connect();
@ -205,18 +216,22 @@ Edge.prototype.draw = function(ctx) {
* @return {boolean} True if location is located on the edge
*/
Edge.prototype.isOverlappingWith = function(obj) {
var distMax = 10;
var xFrom = this.from.x;
var yFrom = this.from.y;
var xTo = this.to.x;
var yTo = this.to.y;
var xObj = obj.left;
var yObj = obj.top;
var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
return (dist < distMax);
if (this.connected == true) {
var distMax = 10;
var xFrom = this.from.x;
var yFrom = this.from.y;
var xTo = this.to.x;
var yTo = this.to.y;
var xObj = obj.left;
var yObj = obj.top;
var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
return (dist < distMax);
}
else {
return false
}
};
@ -229,7 +244,8 @@ Edge.prototype.isOverlappingWith = function(obj) {
*/
Edge.prototype._drawLine = function(ctx) {
// set style
ctx.strokeStyle = this.color;
if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
else {ctx.strokeStyle = this.color.color;}
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
@ -359,7 +375,9 @@ Edge.prototype._label = function (ctx, text, x, y) {
*/
Edge.prototype._drawDashLine = function(ctx) {
// set style
ctx.strokeStyle = this.color;
if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
else {ctx.strokeStyle = this.color.color;}
ctx.lineWidth = this._getLineWidth();
// only firefox and chrome support this method, else we use the legacy one.
@ -482,8 +500,8 @@ Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
Edge.prototype._drawArrowCenter = function(ctx) {
var point;
// set style
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
ctx.lineWidth = this._getLineWidth();
if (this.from != this.to) {
@ -556,8 +574,9 @@ Edge.prototype._drawArrowCenter = function(ctx) {
*/
Edge.prototype._drawArrow = function(ctx) {
// set style
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
ctx.lineWidth = this._getLineWidth();
var angle, length;

+ 131
- 46
src/graph/Graph.js View File

@ -22,7 +22,8 @@ function Graph (container, data, options) {
this.renderRefreshRate = 60; // hz (fps)
this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
this.renderTime = 0.5 * this.renderTimestep; // measured time it takes to render a frame
this.maxRenderSteps = 3; // max amount of physics ticks per render step.
this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
this.physicsDiscreteStepsize = 0.65; // discrete stepsize of the simulation
this.stabilize = true; // stabilize before displaying the graph
this.selectable = true;
@ -63,7 +64,10 @@ function Graph (container, data, options) {
widthMax: 15,
width: 1,
style: 'line',
color: '#848484',
color: {
color:'#848484',
highlight:'#848484'
},
fontColor: '#343434',
fontSize: 14, // px
fontFace: 'arial',
@ -80,8 +84,8 @@ function Graph (container, data, options) {
theta: 1 / 0.6, // inverted to save time during calculation
gravitationalConstant: -2000,
centralGravity: 0.3,
springLength: 100,
springConstant: 0.05,
springLength: 95,
springConstant: 0.04,
damping: 0.09
},
repulsion: {
@ -142,10 +146,11 @@ function Graph (container, data, options) {
nodeSpacing: 100,
direction: "UD" // UD, DU, LR, RL
},
freezeForStabilization: false,
smoothCurves: true,
maxVelocity: 10,
minVelocity: 0.1, // px/s
maxIterations: 1000 // maximum number of iteration to stabilize
stabilizationIterations: 1000 // maximum number of iteration to stabilize
};
this.editMode = this.constants.dataManipulation.initiallyVisible;
@ -298,6 +303,9 @@ Graph.prototype._getRange = function() {
if (maxY < (node.y)) {maxY = node.y;}
}
}
if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
minY = 0, maxY = 0, minX = 0, maxX = 0;
}
return {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
};
@ -451,7 +459,7 @@ Graph.prototype.setData = function(data, disableStart) {
if (!disableStart) {
// find a stable position or start animating to a stable position
if (this.stabilize) {
this._doStabilize();
this._stabilize();
}
this.start();
}
@ -470,7 +478,9 @@ Graph.prototype.setOptions = function (options) {
if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
if (options.selectable !== undefined) {this.selectable = options.selectable;}
if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
if (options.onAdd) {
this.triggerFunctions.add = options.onAdd;
@ -572,12 +582,28 @@ Graph.prototype.setOptions = function (options) {
if (options.edges) {
for (prop in options.edges) {
if (options.edges.hasOwnProperty(prop)) {
this.constants.edges[prop] = options.edges[prop];
if (typeof options.edges[prop] != "object") {
this.constants.edges[prop] = options.edges[prop];
}
}
}
if (options.edges.color !== undefined) {
if (util.isString(options.edges.color)) {
this.constants.edges.color.color = options.edges.color;
this.constants.edges.color.highlight = options.edges.color;
}
else {
if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
}
}
if (!options.edges.fontColor) {
this.constants.edges.fontColor = options.edges.color;
if (options.edges.color !== undefined) {
if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
}
}
// Added to support dashed lines
@ -798,26 +824,24 @@ Graph.prototype._handleDragStart = function() {
}
// create an array with the selected nodes and their original location and status
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
var object = this.selectionObj[objectId];
if (object instanceof Node) {
var s = {
id: object.id,
node: object,
// store original x, y, xFixed and yFixed, make the node temporarily Fixed
x: object.x,
y: object.y,
xFixed: object.xFixed,
yFixed: object.yFixed
};
object.xFixed = true;
object.yFixed = true;
drag.selection.push(s);
}
for (var objectId in this.selectionObj.nodes) {
if (this.selectionObj.nodes.hasOwnProperty(objectId)) {
var object = this.selectionObj.nodes[objectId];
var s = {
id: object.id,
node: object,
// store original x, y, xFixed and yFixed, make the node temporarily Fixed
x: object.x,
y: object.y,
xFixed: object.xFixed,
yFixed: object.yFixed
};
object.xFixed = true;
object.yFixed = true;
drag.selection.push(s);
}
}
}
@ -992,6 +1016,7 @@ Graph.prototype._zoom = function(scale, pointer) {
this.updateClustersDefault();
this._redraw();
return scale;
};
@ -1174,7 +1199,13 @@ Graph.prototype.setSize = function(width, height) {
this.frame.canvas.height = this.frame.canvas.clientHeight;
if (this.manipulationDiv !== undefined) {
this.manipulationDiv.style.width = this.frame.canvas.clientWidth;
this.manipulationDiv.style.width = this.frame.canvas.clientWidth + "px";
}
if (this.navigationDivs !== undefined) {
if (this.navigationDivs['wrapper'] !== undefined) {
this.navigationDivs['wrapper'].style.width = this.frame.canvas.clientWidth + "px";
this.navigationDivs['wrapper'].style.height = this.frame.canvas.clientHeight + "px";
}
}
this.emit('resize', {width:this.frame.canvas.width,height:this.frame.canvas.height});
@ -1239,16 +1270,13 @@ Graph.prototype._addNodes = function(ids) {
var node = new Node(data, this.images, this.groups, this.constants);
this.nodes[id] = node; // note: this may replace an existing node
if ((node.xFixed == false || node.yFixed == false) && this.createNodeOnClick != true) {
if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) {
var radius = 10 * 0.1*ids.length;
var angle = 2 * Math.PI * Math.random();
if (node.xFixed == false) {node.x = radius * Math.cos(angle);}
if (node.yFixed == false) {node.y = radius * Math.sin(angle);}
// note: no not use node.isMoving() here, as that gives the current
// velocity of the node, which is zero after creation of the node.
this.moving = true;
}
this.moving = true;
}
this._updateNodeIndexList();
this._updateCalculationNodes();
@ -1300,6 +1328,7 @@ Graph.prototype._removeNodes = function(ids) {
delete nodes[id];
}
this._updateNodeIndexList();
this._updateCalculationNodes();
this._reconnectEdges();
this._updateSelection();
this._updateValueRange(nodes);
@ -1692,18 +1721,50 @@ Graph.prototype._drawEdges = function(ctx) {
* Find a stable position for all nodes
* @private
*/
Graph.prototype._doStabilize = function() {
Graph.prototype._stabilize = function() {
if (this.constants.freezeForStabilization == true) {
this._freezeDefinedNodes();
}
// find stable position
var count = 0;
while (this.moving && count < this.constants.maxIterations) {
while (this.moving && count < this.constants.stabilizationIterations) {
this._physicsTick();
count++;
}
this.zoomExtent(false,true);
if (this.constants.freezeForStabilization == true) {
this._restoreFrozenNodes();
}
this.emit("stabilized",{iterations:count});
};
Graph.prototype._freezeDefinedNodes = function() {
var nodes = this.nodes;
for (var id in nodes) {
if (nodes.hasOwnProperty(id)) {
if (nodes[id].x != null && nodes[id].y != null) {
nodes[id].fixedData.x = nodes[id].xFixed;
nodes[id].fixedData.y = nodes[id].yFixed;
nodes[id].xFixed = true;
nodes[id].yFixed = true;
}
}
}
};
Graph.prototype._restoreFrozenNodes = function() {
var nodes = this.nodes;
for (var id in nodes) {
if (nodes.hasOwnProperty(id)) {
if (nodes[id].fixedData.x != null) {
nodes[id].xFixed = nodes[id].fixedData.x;
nodes[id].yFixed = nodes[id].fixedData.y;
}
}
}
};
/**
@ -1730,14 +1791,16 @@ Graph.prototype._isMoving = function(vmin) {
* @private
*/
Graph.prototype._discreteStepNodes = function() {
var interval = 0.65;
var interval = this.physicsDiscreteStepsize;
var nodes = this.nodes;
var nodeId;
var nodesPresent = false;
if (this.constants.maxVelocity > 0) {
for (nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity);
nodesPresent = true;
}
}
}
@ -1745,15 +1808,19 @@ Graph.prototype._discreteStepNodes = function() {
for (nodeId in nodes) {
if (nodes.hasOwnProperty(nodeId)) {
nodes[nodeId].discreteStep(interval);
nodesPresent = true;
}
}
}
var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05);
if (vminCorrected > 0.5*this.constants.maxVelocity) {
this.moving = true;
}
else {
this.moving = this._isMoving(vminCorrected);
if (nodesPresent == true) {
var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05);
if (vminCorrected > 0.5*this.constants.maxVelocity) {
this.moving = true;
}
else {
this.moving = this._isMoving(vminCorrected);
}
}
};
@ -1762,10 +1829,10 @@ Graph.prototype._physicsTick = function() {
if (!this.freezeSimulation) {
if (this.moving) {
this._doInAllActiveSectors("_initializeForceCalculation");
this._doInAllActiveSectors("_discreteStepNodes");
if (this.constants.smoothCurves) {
this._doInSupportSector("_discreteStepNodes");
}
this._doInAllActiveSectors("_discreteStepNodes");
this._findCenter(this._getRange())
}
}
@ -1792,7 +1859,7 @@ Graph.prototype._animationStep = function() {
var maxSteps = 1;
this._physicsTick();
var timeRequired = Date.now() - calculationTime;
while (timeRequired < (this.renderTimestep - this.renderTime) && maxSteps < this.maxRenderSteps) {
while (timeRequired < (this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) {
this._physicsTick();
timeRequired = Date.now() - calculationTime;
maxSteps++;
@ -1899,6 +1966,7 @@ Graph.prototype._createBezierNodes = function() {
{id:nodeId,
mass:1,
shape:'circle',
image:"",
internalMultiplier:1
},{},{},this.constants);
edge.via = this.sectors['support']['nodes'][nodeId];
@ -1919,6 +1987,23 @@ Graph.prototype._initializeMixinLoaders = function () {
}
};
/**
* Load the XY positions of the nodes into the dataset.
*/
Graph.prototype.storePosition = function() {
var dataArray = [];
for (var nodeId in this.nodes) {
if (this.nodes.hasOwnProperty(nodeId)) {
var node = this.nodes[nodeId];
var allowedToMoveX = !this.nodes.xFixed;
var allowedToMoveY = !this.nodes.yFixed;
if (this.nodesData.data[nodeId].x != Math.round(node.x) || this.nodesData.data[nodeId].y != Math.round(node.y)) {
dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY});
}
}
}
this.nodesData.update(dataArray);
};

+ 12
- 7
src/graph/Node.js View File

@ -42,8 +42,8 @@ function Node(properties, imagelist, grouplist, constants) {
this.id = undefined;
this.shape = constants.nodes.shape;
this.image = constants.nodes.image;
this.x = 0;
this.y = 0;
this.x = null;
this.y = null;
this.xFixed = false;
this.yFixed = false;
this.horizontalAlignLeft = true; // these are for the navigation controls
@ -66,6 +66,7 @@ function Node(properties, imagelist, grouplist, constants) {
this.minForce = constants.minForce;
this.damping = constants.physics.damping;
this.mass = 1; // kg
this.fixedData = {x:null,y:null};
this.setProperties(properties, constants);
@ -149,8 +150,6 @@ Node.prototype.setProperties = function(properties, constants) {
// physics
if (properties.internalMultiplier !== undefined) {this.internalMultiplier = properties.internalMultiplier;}
if (properties.damping !== undefined) {this.dampingBase = properties.damping;}
if (properties.mass !== undefined) {this.mass = properties.mass;}
// navigation controls properties
@ -182,7 +181,7 @@ Node.prototype.setProperties = function(properties, constants) {
if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;}
if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;}
if (this.image !== undefined) {
if (this.image !== undefined && this.image != "") {
if (this.imagelist) {
this.imageObj = this.imagelist.load(this.image);
}
@ -191,8 +190,8 @@ Node.prototype.setProperties = function(properties, constants) {
}
}
this.xFixed = this.xFixed || (properties.x !== undefined && !properties.allowedToMove);
this.yFixed = this.yFixed || (properties.y !== undefined && !properties.allowedToMove);
this.xFixed = this.xFixed || (properties.x !== undefined && !properties.allowedToMoveX);
this.yFixed = this.yFixed || (properties.y !== undefined && !properties.allowedToMoveY);
this.radiusFixed = this.radiusFixed || (properties.radius !== undefined);
if (this.shape == 'image') {
@ -421,6 +420,9 @@ Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx;
this.x += this.vx * interval; // position
}
else {
this.fx = 0;
}
if (!this.yFixed) {
var dy = this.damping * this.vy; // damping force
@ -429,6 +431,9 @@ Node.prototype.discreteStepLimited = function(interval, maxVelocity) {
this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy;
this.y += this.vy * interval; // position
}
else {
this.fy = 0;
}
};
/**

+ 18
- 14
src/graph/css/graph-navigation.css View File

@ -25,38 +25,42 @@ div.graph-navigation:active {
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
}
div.graph-navigation.active {
box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
}
div.graph-navigation.up {
background-image: url("img/graph/upArrow.png");
margin-top:520px;
margin-left:55px;
bottom:50px;
left:55px;
}
div.graph-navigation.down {
background-image: url("img/graph/downArrow.png");
margin-top:560px;
margin-left:55px;
bottom:10px;
left:55px;
}
div.graph-navigation.left {
background-image: url("img/graph/leftArrow.png");
margin-top:560px;
margin-left:15px;
bottom:10px;
left:15px;
}
div.graph-navigation.right {
background-image: url("img/graph/rightArrow.png");
margin-top:560px;
margin-left:95px;
bottom:10px;
left:95px;
}
div.graph-navigation.zoomIn {
background-image: url("img/graph/plus.png");
margin-top:560px;
margin-left:555px;
bottom:10px;
right:15px;
}
div.graph-navigation.zoomOut {
background-image: url("img/graph/minus.png");
margin-top:560px;
margin-left:515px;
bottom:10px;
right:55px;
}
div.graph-navigation.zoomExtends {
background-image: url("img/graph/zoomExtends.png");
margin-top:520px;
margin-left:555px;
bottom:50px;
right:15px;
}

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

@ -22,7 +22,7 @@ var ClusterMixin = {
// this is called here because if clusterin is disabled, the start and stabilize are called in
// the setData function.
if (this.stabilize) {
this._doStabilize();
this._stabilize();
}
this.start();
},
@ -1044,8 +1044,8 @@ var ClusterMixin = {
repositionNodes : function() {
for (var i = 0; i < this.nodeIndices.length; i++) {
var node = this.nodes[this.nodeIndices[i]];
if ((node.xFixed == false || node.yFixed == false) && this.createNodeOnClick != true) {
var radius = this.constants.physics.springLength * Math.min(100,node.mass);
if ((node.xFixed == false || node.yFixed == false)) {
var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.mass);
var angle = 2 * Math.PI * Math.random();
if (node.xFixed == false) {node.x = radius * Math.cos(angle);}
if (node.yFixed == false) {node.y = radius * Math.sin(angle);}

+ 4
- 1
src/graph/graphMixins/HierarchicalLayoutMixin.js View File

@ -12,6 +12,9 @@ var HierarchicalLayoutMixin = {
if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") {
this.constants.hierarchicalLayout.levelSeparation *= -1;
}
else {
this.constants.hierarchicalLayout.levelSeparation = Math.abs(this.constants.hierarchicalLayout.levelSeparation);
}
// get the size of the largest hubs and check if the user has defined a level for a node.
var hubsize = 0;
var node, nodeId;
@ -96,7 +99,7 @@ var HierarchicalLayoutMixin = {
}
// stabilize the system after positioning. This function calls zoomExtent.
this._doStabilize();
this._stabilize();
},

+ 10
- 8
src/graph/graphMixins/ManipulationMixin.js View File

@ -62,7 +62,9 @@ var manipulationMixin = {
*/
_createManipulatorBar : function() {
// remove bound functions
this.off('select', this.boundFunction);
if (this.boundFunction) {
this.off('select', this.boundFunction);
}
// restore overloaded functions
this._restoreOverloadedFunctions();
@ -137,7 +139,9 @@ var manipulationMixin = {
_createAddNodeToolbar : function() {
// clear the toolbar
this._clearManipulatorBar();
this.off('select', this.boundFunction);
if (this.boundFunction) {
this.off('select', this.boundFunction);
}
// create the toolbar contents
this.manipulationDiv.innerHTML = "" +
@ -168,7 +172,9 @@ var manipulationMixin = {
this._unselectAll(true);
this.freezeSimulation = true;
this.off('select', this.boundFunction);
if (this.boundFunction) {
this.off('select', this.boundFunction);
}
this._unselectAll();
this.forceAppendSelection = false;
@ -288,14 +294,12 @@ var manipulationMixin = {
_addNode : function() {
if (this._selectionIsEmpty() && this.editMode == true) {
var positionObject = this._pointerToPositionObject(this.pointerPosition);
var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMove:true};
var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true};
if (this.triggerFunctions.add) {
if (this.triggerFunctions.add.length == 2) {
var me = this;
this.triggerFunctions.add(defaultData, function(finalizedData) {
me.createNodeOnClick = true;
me.nodesData.add(finalizedData);
me.createNodeOnClick = false;
me._createManipulatorBar();
me.moving = true;
me.start();
@ -309,9 +313,7 @@ var manipulationMixin = {
}
}
else {
this.createNodeOnClick = true;
this.nodesData.add(defaultData);
this.createNodeOnClick = false;
this._createManipulatorBar();
this.moving = true;
this.start();

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

@ -96,7 +96,7 @@ var graphMixinLoaders = {
* @private
*/
_loadSelectionSystem : function() {
this.selectionObj = { };
this.selectionObj = {nodes:{},edges:{}};
this._loadMixin(SelectionMixin);
},

+ 33
- 1
src/graph/graphMixins/NavigationMixin.js View File

@ -30,6 +30,9 @@ var NavigationMixin = {
this.navigationDivs['wrapper'] = document.createElement('div');
this.navigationDivs['wrapper'].id = "graph-navigation_wrapper";
this.navigationDivs['wrapper'].style.position = "absolute";
this.navigationDivs['wrapper'].style.width = this.frame.canvas.clientWidth + "px";
this.navigationDivs['wrapper'].style.height = this.frame.canvas.clientHeight + "px";
this.containerElement.insertBefore(this.navigationDivs['wrapper'],this.frame);
for (var i = 0; i < navigationDivs.length; i++) {
@ -81,10 +84,12 @@ var NavigationMixin = {
* @private
*/
_moveUp : function(event) {
console.log("here")
this.yIncrement = this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['up'].className += " active";
}
},
@ -96,6 +101,9 @@ var NavigationMixin = {
this.yIncrement = -this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['down'].className += " active";
}
},
@ -107,6 +115,9 @@ var NavigationMixin = {
this.xIncrement = this.constants.keyboard.speed.x;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['left'].className += " active";
}
},
@ -118,6 +129,9 @@ var NavigationMixin = {
this.xIncrement = -this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['right'].className += " active";
}
},
@ -129,6 +143,9 @@ var NavigationMixin = {
this.zoomIncrement = this.constants.keyboard.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['zoomIn'].className += " active";
}
},
@ -140,6 +157,9 @@ var NavigationMixin = {
this.zoomIncrement = -this.constants.keyboard.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done
this._preventDefault(event);
if (this.navigationDivs) {
this.navigationDivs['zoomOut'].className += " active";
}
},
@ -149,6 +169,10 @@ var NavigationMixin = {
*/
_stopZoom : function() {
this.zoomIncrement = 0;
if (this.navigationDivs) {
this.navigationDivs['zoomIn'].className = this.navigationDivs['zoomIn'].className.replace(" active","");
this.navigationDivs['zoomOut'].className = this.navigationDivs['zoomOut'].className.replace(" active","");
}
},
@ -158,6 +182,10 @@ var NavigationMixin = {
*/
_yStopMoving : function() {
this.yIncrement = 0;
if (this.navigationDivs) {
this.navigationDivs['up'].className = this.navigationDivs['up'].className.replace(" active","");
this.navigationDivs['down'].className = this.navigationDivs['down'].className.replace(" active","");
}
},
@ -167,6 +195,10 @@ var NavigationMixin = {
*/
_xStopMoving : function() {
this.xIncrement = 0;
if (this.navigationDivs) {
this.navigationDivs['left'].className = this.navigationDivs['left'].className.replace(" active","");
this.navigationDivs['right'].className = this.navigationDivs['right'].className.replace(" active","");
}
}

+ 72
- 60
src/graph/graphMixins/SelectionMixin.js View File

@ -131,7 +131,13 @@ var SelectionMixin = {
* @private
*/
_addToSelection : function(obj) {
this.selectionObj[obj.id] = obj;
if (obj instanceof Node) {
this.selectionObj.nodes[obj.id] = obj;
}
else {
this.selectionObj.edges[obj.id] = obj;
}
},
@ -142,7 +148,12 @@ var SelectionMixin = {
* @private
*/
_removeFromSelection : function(obj) {
delete this.selectionObj[obj.id];
if (obj instanceof Node) {
delete this.selectionObj.nodes[obj.id];
}
else {
delete this.selectionObj.edges[obj.id];
}
},
@ -156,13 +167,18 @@ var SelectionMixin = {
if (doNotTrigger === undefined) {
doNotTrigger = false;
}
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
this.selectionObj[objectId].unselect();
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
this.selectionObj.nodes[nodeId].unselect();
}
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
this.selectionObj.edges[edgeId].unselect();;
}
}
this.selectionObj = {};
this.selectionObj = {nodes:{},edges:{}};
if (doNotTrigger == false) {
this.emit('select', this.getSelection());
@ -180,13 +196,11 @@ var SelectionMixin = {
doNotTrigger = false;
}
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
if (this.selectionObj[objectId].clusterSize > 1) {
this.selectionObj[objectId].unselect();
this._removeFromSelection(this.selectionObj[objectId]);
}
for (var nodeId in this.selectionObj.nodes) {
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
if (this.selectionObj.nodes[nodeId].clusterSize > 1) {
this.selectionObj.nodes[nodeId].unselect();
this._removeFromSelection(this.selectionObj.nodes[nodeId]);
}
}
}
@ -205,11 +219,9 @@ var SelectionMixin = {
*/
_getSelectedNodeCount : function() {
var count = 0;
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
count += 1;
}
for (var nodeId in this.selectionObj.nodes) {
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
count += 1;
}
}
return count;
@ -222,11 +234,9 @@ var SelectionMixin = {
* @private
*/
_getSelectedNode : function() {
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
return this.selectionObj[objectId];
}
for (var nodeId in this.selectionObj.nodes) {
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
return this.selectionObj.nodes[nodeId];
}
}
return null;
@ -241,11 +251,9 @@ var SelectionMixin = {
*/
_getSelectedEdgeCount : function() {
var count = 0;
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Edge) {
count += 1;
}
for (var edgeId in this.selectionObj.edges) {
if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
count += 1;
}
}
return count;
@ -260,8 +268,13 @@ var SelectionMixin = {
*/
_getSelectedObjectCount : function() {
var count = 0;
for (var objectId in this.selectionObj) {
if (this.selectionObj.hasOwnProperty(objectId)) {
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
count += 1;
}
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
count += 1;
}
}
@ -275,8 +288,13 @@ var SelectionMixin = {
* @private
*/
_selectionIsEmpty : function() {
for(var objectId in this.selectionObj) {
if(this.selectionObj.hasOwnProperty(objectId)) {
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
return false;
}
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
return false;
}
}
@ -291,12 +309,10 @@ var SelectionMixin = {
* @private
*/
_clusterInSelection : function() {
for(var objectId in this.selectionObj) {
if(this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
if (this.selectionObj[objectId].clusterSize > 1) {
return true;
}
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
if (this.selectionObj.nodes[nodeId].clusterSize > 1) {
return true;
}
}
}
@ -477,11 +493,9 @@ var SelectionMixin = {
*/
getSelectedNodes : function() {
var idArray = [];
for(var objectId in this.selectionObj) {
if(this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
idArray.push(objectId);
}
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
idArray.push(nodeId);
}
}
return idArray
@ -495,14 +509,12 @@ var SelectionMixin = {
*/
getSelectedEdges : function() {
var idArray = [];
for(var objectId in this.selectionObj) {
if(this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Edge) {
idArray.push(objectId);
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
idArray.push(edgeId);
}
}
return idArray
return idArray;
},
@ -538,17 +550,17 @@ var SelectionMixin = {
* @private
*/
_updateSelection : function () {
for(var objectId in this.selectionObj) {
if(this.selectionObj.hasOwnProperty(objectId)) {
if (this.selectionObj[objectId] instanceof Node) {
if (!this.nodes.hasOwnProperty(objectId)) {
delete this.selectionObj[objectId];
}
for(var nodeId in this.selectionObj.nodes) {
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) {
if (!this.nodes.hasOwnProperty(nodeId)) {
delete this.selectionObj.nodes[nodeId];
}
else { // assuming only edges and nodes are selected
if (!this.edges.hasOwnProperty(objectId)) {
delete this.selectionObj[objectId];
}
}
}
for(var edgeId in this.selectionObj.edges) {
if(this.selectionObj.edges.hasOwnProperty(edgeId)) {
if (!this.edges.hasOwnProperty(edgeId)) {
delete this.selectionObj.edges[edgeId];
}
}
}

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

@ -133,6 +133,7 @@ var barnesHutMixin = {
mass:0,
range: {minX:centerX-halfRootSize,maxX:centerX+halfRootSize,
minY:centerY-halfRootSize,maxY:centerY+halfRootSize},
size: rootSize,
calcSize: 1 / rootSize,
children: {data:null},
@ -209,7 +210,6 @@ var barnesHutMixin = {
parentBranch.children[region].children.data.y == node.y) {
node.x += Math.random();
node.y += Math.random();
this._placeInTree(parentBranch,node, true);
}
else {
this._splitBranch(parentBranch.children[region]);

+ 170
- 21
src/graph/graphMixins/physics/PhysicsMixin.js View File

@ -98,7 +98,6 @@ var physicsMixin = {
this._calculateGravitationalForces();
this._calculateNodeForces();
if (this.constants.smoothCurves == true) {
this._calculateSpringForcesWithSupport();
}
@ -170,8 +169,8 @@ var physicsMixin = {
dx = -node.x;
dy = -node.y;
distance = Math.sqrt(dx*dx + dy*dy);
gravityForce = gravity / distance;
gravityForce = (distance == 0) ? 0 : (gravity / distance);
node.fx = dx * gravityForce;
node.fy = dy * gravityForce;
}
@ -280,12 +279,12 @@ var physicsMixin = {
dy = (node1.y - node2.y);
length = Math.sqrt(dx * dx + dy * dy);
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
if (length == 0) {
length = 0.01;
}
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
fx = dx * springForce;
fy = dy * springForce;
@ -302,6 +301,9 @@ var physicsMixin = {
*/
_loadPhysicsConfiguration : function() {
if (this.physicsConfiguration === undefined) {
this.backupConstants = {};
util.copyObject(this.constants,this.backupConstants);
var hierarchicalLayoutDirections = ["LR","RL","UD","DU"];
this.physicsConfiguration = document.createElement('div');
this.physicsConfiguration.className = "PhysicsConfiguration";
@ -370,15 +372,24 @@ var physicsMixin = {
'<td width="150px">direction</td><td>1</td><td><input type="range" min="0" max="3" value="' + hierarchicalLayoutDirections.indexOf(this.constants.hierarchicalLayout.direction) + '" step="1" style="width:300px" id="graph_H_direction"></td><td>4</td><td><input value="' + this.constants.hierarchicalLayout.direction + '" id="graph_H_direction_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">levelSeparation</td><td>1</td><td><input type="range" min="0" max="' + this.constants.hierarchicalLayout.levelSeparation + '" value="150" step="1" style="width:300px" id="graph_H_levsep"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.levelSeparation + '" id="graph_H_levsep_value" style="width:60px"></td>'+
'<td width="150px">levelSeparation</td><td>1</td><td><input type="range" min="0" max="500" value="' + this.constants.hierarchicalLayout.levelSeparation + '" step="1" style="width:300px" id="graph_H_levsep"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.levelSeparation + '" id="graph_H_levsep_value" style="width:60px"></td>'+
'</tr>'+
'<tr>'+
'<td width="150px">nodeSpacing</td><td>1</td><td><input type="range" min="0" max="' + this.constants.hierarchicalLayout.nodeSpacing + '" value="100" step="1" style="width:300px" id="graph_H_nspac"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.nodeSpacing + '" id="graph_H_nspac_value" style="width:60px"></td>'+
'<td width="150px">nodeSpacing</td><td>1</td><td><input type="range" min="0" max="500" value="' + this.constants.hierarchicalLayout.nodeSpacing + '" step="1" style="width:300px" id="graph_H_nspac"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.nodeSpacing + '" id="graph_H_nspac_value" style="width:60px"></td>'+
'</tr>'+
'</table>' +
'<table><tr><td><b>Options:</b></td></tr>' +
'<tr>' +
'<td width="180px"><input type="button" id="graph_toggleSmooth" value="Toggle smoothCurves" style="width:150px"></td>' +
'<td width="180px"><input type="button" id="graph_repositionNodes" value="Reinitialize" style="width:150px"></td>' +
'<td width="180px"><input type="button" id="graph_generateOptions" value="Generate Options" style="width:150px"></td>' +
'</tr>'+
'</table>'
this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement);
this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement);
this.optionsDiv = document.createElement("div");
this.optionsDiv.style.fontSize = "14px";
this.optionsDiv.style.fontFamily = "verdana";
this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement);
var rangeElement;
rangeElement = document.getElementById('graph_BH_gc');
@ -392,7 +403,6 @@ var physicsMixin = {
rangeElement = document.getElementById('graph_BH_damp');
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_damp',1,"physics_damping");
rangeElement = document.getElementById('graph_R_nd');
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_nd',1,"physics_repulsion_nodeDistance");
rangeElement = document.getElementById('graph_R_cg');
@ -424,7 +434,6 @@ var physicsMixin = {
var radioButton1 = document.getElementById("graph_physicsMethod1");
var radioButton2 = document.getElementById("graph_physicsMethod2");
var radioButton3 = document.getElementById("graph_physicsMethod3");
radioButton2.checked = true;
if (this.constants.physics.barnesHut.enabled) {
radioButton1.checked = true;
@ -433,6 +442,17 @@ var physicsMixin = {
radioButton3.checked = true;
}
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
var graph_repositionNodes = document.getElementById("graph_repositionNodes");
var graph_generateOptions = document.getElementById("graph_generateOptions");
graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this);
graph_repositionNodes.onclick = graphRepositionNodes.bind(this);
graph_generateOptions.onclick = graphGenerateOptions.bind(this);
if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";}
else {graph_toggleSmooth.style.background = "#FF8532";}
switchConfigurations.apply(this);
radioButton1.onchange = switchConfigurations.bind(this);
@ -455,6 +475,129 @@ var physicsMixin = {
}
}
function graphToggleSmoothCurves () {
this.constants.smoothCurves = !this.constants.smoothCurves;
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";}
else {graph_toggleSmooth.style.background = "#FF8532";}
this._configureSmoothCurves(false);
};
function graphRepositionNodes () {
for (var nodeId in this.calculationNodes) {
if (this.calculationNodes.hasOwnProperty(nodeId)) {
this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0;
this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0;
}
}
if (this.constants.hierarchicalLayout.enabled == true) {
this._setupHierarchicalLayout();
}
else {
this.repositionNodes();
}
this.moving = true;
this.start();
};
function graphGenerateOptions () {
var options = "No options are required, default values used."
var optionsSpecific = [];
var radioButton1 = document.getElementById("graph_physicsMethod1");
var radioButton2 = document.getElementById("graph_physicsMethod2");
if (radioButton1.checked == true) {
if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);}
if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);}
if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);}
if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);}
if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);}
if (optionsSpecific.length != 0) {
options = "var options = {"
options += "physics: {barnesHut: {"
for (var i = 0; i < optionsSpecific.length; i++) {
options += optionsSpecific[i];
if (i < optionsSpecific.length - 1) {
options += ", "
}
}
options += '}}'
}
if (this.constants.smoothCurves != this.backupConstants.smoothCurves) {
if (optionsSpecific.length == 0) {options = "var options = {";}
else {options += ", "}
options += "smoothCurves: " + this.constants.smoothCurves;
}
if (options != "No options are required, default values used.") {
options += '};'
}
}
else if (radioButton2.checked == true) {
options = "var options = {"
options += "physics: {barnesHut: {enabled: false}";
if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);}
if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);}
if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);}
if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);}
if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);}
if (optionsSpecific.length != 0) {
options += ", repulsion: {"
for (var i = 0; i < optionsSpecific.length; i++) {
options += optionsSpecific[i];
if (i < optionsSpecific.length - 1) {
options += ", "
}
}
options += '}}'
}
if (optionsSpecific.length == 0) {options += "}"}
if (this.constants.smoothCurves != this.backupConstants.smoothCurves) {
options += ", smoothCurves: " + this.constants.smoothCurves;
}
options += '};'
}
else {
options = "var options = {"
if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);}
if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);}
if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);}
if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);}
if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);}
if (optionsSpecific.length != 0) {
options += "physics: {hierarchicalRepulsion: {"
for (var i = 0; i < optionsSpecific.length; i++) {
options += optionsSpecific[i];
if (i < optionsSpecific.length - 1) {
options += ", ";
}
}
options += '}},';
}
options += 'hierarchicalLayout: {';
optionsSpecific = [];
if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);}
if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);}
if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);}
if (optionsSpecific.length != 0) {
for (var i = 0; i < optionsSpecific.length; i++) {
options += optionsSpecific[i];
if (i < optionsSpecific.length - 1) {
options += ", "
}
}
options += '}'
}
else {
options += "enabled:true}";
}
options += '};'
}
this.optionsDiv.innerHTML = options;
};
function switchConfigurations () {
var ids = ["graph_BH_table","graph_R_table","graph_H_table"]
@ -471,43 +614,49 @@ function switchConfigurations () {
this._restoreNodes();
if (radioButton == "R") {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.hierarchicalRepulsion.enabled = false;
this.constants.physics.barnesHut.enabled = false;
}
else if (radioButton == "H") {
this.constants.hierarchicalLayout.enabled = true;
this.constants.physics.hierarchicalRepulsion.enabeled = true;
this.constants.physics.hierarchicalRepulsion.enabled = true;
this.constants.physics.barnesHut.enabled = false;
this._setupHierarchicalLayout();
}
else {
this.constants.hierarchicalLayout.enabled = false;
this.constants.physics.hierarchicalRepulsion.enabeled = false;
this.constants.physics.hierarchicalRepulsion.enabled = false;
this.constants.physics.barnesHut.enabled = true;
}
this._loadSelectedForceSolver();
var graph_toggleSmooth = document.getElementById("graph_toggleSmooth");
if (this.constants.smoothCurves == true) {graph_toggleSmooth.style.background = "#A4FF56";}
else {graph_toggleSmooth.style.background = "#FF8532";}
this.moving = true;
this.start();
}
function showValueOfRange (id,map,constantsVariableName) {
var valueId = id + "_value";
var rangeValue = document.getElementById(id).value;
if (constantsVariableName == "hierarchicalLayout_direction" ||
constantsVariableName == "hierarchicalLayout_levelSeparation" ||
constantsVariableName == "hierarchicalLayout_nodeSpacing") {
this._setupHierarchicalLayout();
}
if (map instanceof Array) {
document.getElementById(valueId).value = map[parseInt(rangeValue)];
this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]);
}
else {
document.getElementById(valueId).value = map * parseFloat(rangeValue);
this._overWriteGraphConstants(constantsVariableName,map * parseFloat(rangeValue));
document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue);
this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue));
}
if (constantsVariableName == "hierarchicalLayout_direction" ||
constantsVariableName == "hierarchicalLayout_levelSeparation" ||
constantsVariableName == "hierarchicalLayout_nodeSpacing") {
this._setupHierarchicalLayout();
}
this.moving = true;
this.start();
};

+ 1
- 1
src/timeline/component/css/groupset.css View File

@ -16,8 +16,8 @@
margin: 0;
border-right: 1px solid #bfbfbf;
box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.vis.timeline .labels .label-set {

+ 1
- 0
src/timeline/component/css/item.css View File

@ -66,6 +66,7 @@
border-width: 1px;
border-radius: 2px;
-moz-border-radius: 2px; /* For Firefox 3.6 and older */
-moz-box-sizing: border-box;
box-sizing: border-box;
}

+ 14
- 0
src/util.js View File

@ -826,3 +826,17 @@ util.isValidHex = function isValidHex(hex) {
var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
return isOk;
}
util.copyObject = function copyObject(objectFrom,objectTo) {
for (var i in objectFrom) {
if (objectFrom.hasOwnProperty(i)) {
if (typeof objectFrom[i] == "object") {
objectTo[i] = {};
util.copyObject(objectFrom[i],objectTo[i]);
}
else {
objectTo[i] = objectFrom[i];
}
}
}
}

Loading…
Cancel
Save