Commit 8d416485 authored by Romain Loth's avatar Romain Loth

add a "disperse nodes" button for noverlap in zoom/layout controls

parent 2493378f
......@@ -373,6 +373,13 @@
</a>
</li>
<li>
<a href="#" id="noverlapButton" class="zoombarbuttons"
title="Disperse Overlapping Nodes">
<img src="libs/img2/disperse.png" style="width:36px">
</a>
</li>
<li>
<div class="onoffswitch">
<input type="checkbox" name="edges-switch" class="onoffswitch-checkbox" id="edges-switch" checked>
......@@ -617,6 +624,8 @@
the v1.2 stock sigma.(min).js but with a much lighter RAM footprint!
cf. https://github.com/jacomyal/sigma.js/issues/340
-->
<script src="tinawebJS/sigma_v1.2/plugins/sigma.plugins.animate/sigma.plugins.animate.js"></script>
<script src="tinawebJS/sigma_v1.2/plugins/sigma.layout.noverlap/sigma.layout.noverlap.js"></script>
<script src="tinawebJS/sigma_v1.2/plugins/sigma.layout.forceAtlas2/supervisor.js"></script>
<script src="tinawebJS/sigma_v1.2/plugins/sigma.layout.forceAtlas2/worker.js"></script>
<script src="tinawebJS/sigma_v1.2/plugins/sigma.renderers.snapshot/sigma.renderers.snapshot.js"></script>
......
This diff is collapsed.
......@@ -822,6 +822,20 @@ TinaWebJS = function ( sigmacanvas ) {
}
});
$("#noverlapButton").click(function () {
if(! TW.partialGraph.isNoverlapRunning()) {
// show waiting cursor on page and button
theHtml.classList.add('waiting');
$("#noverlapButton").css('cursor', 'wait')
var listener = TW.partialGraph.startNoverlap();
listener.bind('stop', function(event) {
theHtml.classList.remove('waiting');
$("#noverlapButton").css('cursor', 'auto')
});
return;
}
});
$("#edges-switch").click(function () {
sigma_utils.toggleEdges()
......
......@@ -677,6 +677,19 @@ else {
// init FA2 for any future forceAtlas2 calls
TW.partialGraph.configForceAtlas2(TW.FA2Params)
// init noverlap for any future calls
TW.partialGraph.configNoverlap({
nodeMargin: .3,
scaleNodes: 1.2,
gridSize: 400,
speed: 5,
maxIterations: 10,
easing: 'quadraticOut', // animation transition function
duration: 1500 // animation duration
// NB animation happens *after* processing
});
// REFA new sigma.js
TW.partialGraph.camera.goTo({x:0, y:0, ratio:0.5, angle: 0})
......@@ -685,9 +698,11 @@ else {
// run fa2 if settings_explorerjs.fa2enabled == true
if (fa2enabled) {
TW.partialGraph.startForceAtlas2();
$.doTimeout(parseInt(fa2milliseconds) || 5000, function(){
setTimeout(function(){
TW.partialGraph.stopForceAtlas2();
});
},
parseInt(fa2milliseconds) || 5000
);
}
if( TW.categories.length==1 ) {
......
sigma.layout.noverlap
========================
Plugin developed by [Andrew Pitts](https://github.com/apitts) and published under the [MIT](LICENSE) license. Original algorithm by [Mathieu Jacomy](https://github.com/jacomyma) and ported to sigma.js with permission.
---
This plugin runs an algorithm which distributes nodes in the network, ensuring that they do not overlap and providing a margin where specified.
## Methods
**configure**
Changes the layout's configuration.
```js
var listener = s.configNoverlap(config);
```
**start**
Starts the layout. It is possible to pass a configuration if this is the first time you start the layout.
```js
s.startNoverlap();
```
**isRunning**
Returns whether the layout is running.
```js
s.isNoverlapRunning();
```
## Configuration
* **nodes**: *array*: the subset of nodes to apply the layout.
*Algorithm configuration*
* **nodeMargin**: *number* `5.0`: The additional minimum space to apply around each and every node.
* **scaleNodes**: *number* `1.2`: A multiplier to apply to nodes such that larger nodes will have more space around them if this multiplier is greater than zero.
* **gridSize**: *integer* `20`: The number of rows and columns to use when dividing the nodes up into cells which the algorithm is applied to. Use more rows and columns for larger graphs for a more efficient algorithm.
* **permittedExpansion** *number* `1.1`: At every step, this is the maximum ratio to apply to the bounding box, i.e. the maximum by which the network is permitted to expand.
* **rendererIndex** *integer* `0`: The index of the renderer to use to compute overlap and collisions of the nodes.
* **speed** *number* `2`: A larger value increases the speed with which the algorithm will convergence at the cost of precision.
* **maxIterations** *number* `500`: The maximum number of iterations to run the algorithm for before stopping it.
*Easing configuration*
* **easing** *string*: if specified, ease the transition between nodes positions if background is `true`. The duration is specified by the Sigma settings `animationsTime`. See [sigma.utils.easing](../../src/utils/sigma.utils.js#L723) for available values.
* **duration** *number*: duration of the transition for the easing method. Default value is Sigma setting `animationsTime`.
## Events
The plugin dispatches the following events:
- `start`: on layout start.
- `interpolate`: at the beginning of the layout animation if an *easing* function is specified and the layout is ran on background.
- `stop`: on layout stop, will be dispatched after `interpolate`.
Example:
```js
s = new sigma({
graph: g,
container: 'graph-container'
});
var config = {
nodeMargin: 3.0,
scaleNodes: 1.3
};
// Configure the algorithm
var listener = s.configNoverlap(config);
// Bind all events:
listener.bind('start stop interpolate', function(event) {
console.log(event.type);
});
// Start the algorithm:
s.startNoverlap();
```
\ No newline at end of file
sigma.plugins.animate
=====================
Plugin developed by [Alexis Jacomy](https://github.com/jacomyal).
---
This plugin provides a method to animate a sigma instance by interpolating some node properties. Check the `sigma.plugins.animate` function doc or the `examples/animate.html` code sample to know more.
Interpolate coordinates as follows:
```js
sigma.plugins.animate(
s,
{
x: 'target_x',
y: 'target_y',
}
);
```
Interpolate colors and sizes as follows:
```js
sigma.plugins.animate(
s,
{
size: 'target_size',
color: 'target_color'
}
);
```
Animate a subset of nodes as follows:
```js
sigma.plugins.animate(
s,
{
x: 'to_x',
y: 'to_y',
size: 'to_size',
color: 'to_color'
},
{
nodes: ['n0', 'n1', 'n2']
}
);
```
Example using all options:
```js
sigma.plugins.animate(
s,
{
x: 'to_x',
y: 'to_y',
size: 'to_size',
color: 'to_color'
},
{
nodes: ['n0', 'n1', 'n2'],
easing: 'cubicInOut',
duration: 300,
onComplete: function() {
// do stuff here after animation is complete
}
}
);
```
/**
* This plugin provides a method to animate a sigma instance by interpolating
* some node properties. Check the sigma.plugins.animate function doc or the
* examples/animate.html code sample to know more.
*/
(function() {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
sigma.utils.pkg('sigma.plugins');
var _id = 0,
_cache = {};
// TOOLING FUNCTIONS:
// ******************
function parseColor(val) {
if (_cache[val])
return _cache[val];
var result = [0, 0, 0];
if (val.match(/^#/)) {
val = (val || '').replace(/^#/, '');
result = (val.length === 3) ?
[
parseInt(val.charAt(0) + val.charAt(0), 16),
parseInt(val.charAt(1) + val.charAt(1), 16),
parseInt(val.charAt(2) + val.charAt(2), 16)
] :
[
parseInt(val.charAt(0) + val.charAt(1), 16),
parseInt(val.charAt(2) + val.charAt(3), 16),
parseInt(val.charAt(4) + val.charAt(5), 16)
];
} else if (val.match(/^ *rgba? *\(/)) {
val = val.match(
/^ *rgba? *\( *([0-9]*) *, *([0-9]*) *, *([0-9]*) *(,.*)?\) *$/
);
result = [
+val[1],
+val[2],
+val[3]
];
}
_cache[val] = {
r: result[0],
g: result[1],
b: result[2]
};
return _cache[val];
}
function interpolateColors(c1, c2, p) {
c1 = parseColor(c1);
c2 = parseColor(c2);
var c = {
r: c1.r * (1 - p) + c2.r * p,
g: c1.g * (1 - p) + c2.g * p,
b: c1.b * (1 - p) + c2.b * p
};
return 'rgb(' + [c.r | 0, c.g | 0, c.b | 0].join(',') + ')';
}
/**
* This function will animate some specified node properties. It will
* basically call requestAnimationFrame, interpolate the values and call the
* refresh method during a specified duration.
*
* Recognized parameters:
* **********************
* Here is the exhaustive list of every accepted parameters in the settings
* object:
*
* {?array} nodes An array of node objects or node ids. If
* not specified, all nodes of the graph
* will be animated.
* {?(function|string)} easing Either the name of an easing in the
* sigma.utils.easings package or a
* function. If not specified, the
* quadraticInOut easing from this package
* will be used instead.
* {?number} duration The duration of the animation. If not
* specified, the "animationsTime" setting
* value of the sigma instance will be used
* instead.
* {?function} onComplete Eventually a function to call when the
* animation is ended.
*
* @param {sigma} s The related sigma instance.
* @param {object} animate An hash with the keys being the node properties
* to interpolate, and the values being the related
* target values.
* @param {?object} options Eventually an object with options.
*/
sigma.plugins.animate = function(s, animate, options) {
var o = options || {},
id = ++_id,
duration = o.duration || s.settings('animationsTime'),
easing = typeof o.easing === 'string' ?
sigma.utils.easings[o.easing] :
typeof o.easing === 'function' ?
o.easing :
sigma.utils.easings.quadraticInOut,
start = sigma.utils.dateNow(),
nodes,
startPositions;
if (o.nodes && o.nodes.length) {
if (typeof o.nodes[0] === 'object')
nodes = o.nodes;
else
nodes = s.graph.nodes(o.nodes); // argument is an array of IDs
}
else
nodes = s.graph.nodes();
// Store initial positions:
startPositions = nodes.reduce(function(res, node) {
var k;
res[node.id] = {};
for (k in animate)
if (k in node)
res[node.id][k] = node[k];
return res;
}, {});
s.animations = s.animations || Object.create({});
sigma.plugins.kill(s);
// Do not refresh edgequadtree during drag:
var k,
c;
for (k in s.cameras) {
c = s.cameras[k];
c.edgequadtree._enabled = false;
}
function step() {
var p = (sigma.utils.dateNow() - start) / duration;
if (p >= 1) {
nodes.forEach(function(node) {
for (var k in animate)
if (k in animate)
node[k] = node[animate[k]];
});
// Allow to refresh edgequadtree:
var k,
c;
for (k in s.cameras) {
c = s.cameras[k];
c.edgequadtree._enabled = true;
}
s.refresh();
if (typeof o.onComplete === 'function')
o.onComplete();
} else {
p = easing(p);
nodes.forEach(function(node) {
for (var k in animate)
if (k in animate) {
if (k.match(/color$/))
node[k] = interpolateColors(
startPositions[node.id][k],
node[animate[k]],
p
);
else
node[k] =
node[animate[k]] * p +
startPositions[node.id][k] * (1 - p);
}
});
s.refresh();
s.animations[id] = requestAnimationFrame(step);
}
}
step();
};
sigma.plugins.kill = function(s) {
for (var k in (s.animations || {}))
cancelAnimationFrame(s.animations[k]);
// Allow to refresh edgequadtree:
var k,
c;
for (k in s.cameras) {
c = s.cameras[k];
c.edgequadtree._enabled = true;
}
};
}).call(window);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment