mirror of https://gitee.com/bigwinds/arangodb
graph ui devel
This commit is contained in:
parent
068a43c75d
commit
bedab04286
|
@ -58,6 +58,8 @@
|
|||
"frontend/js/lib/typeahead.bundle.min.js",
|
||||
"frontend/js/lib/numeral.min.js",
|
||||
"frontend/js/lib/sigma.min.js",
|
||||
"frontend/js/lib/sigma.plugins.animate.js",
|
||||
"frontend/js/lib/sigma.layout.noverlap.js",
|
||||
"frontend/js/lib/jsoneditor-min.js",
|
||||
"frontend/js/lib/strftime-min.js",
|
||||
"frontend/js/lib/d3.fisheye.min.js",
|
||||
|
|
|
@ -294,3 +294,73 @@ authRouter.get('/job', function(req, res) {
|
|||
.description(dd`
|
||||
This function returns the job ids of all currently running jobs.
|
||||
`);
|
||||
|
||||
|
||||
authRouter.get('/graph/:name', function(req, res) {
|
||||
var _ = require("lodash");
|
||||
var name = req.pathParams.name;
|
||||
var gm = require("@arangodb/general-graph");
|
||||
//var traversal = require("@arangodb/graph/traversal");
|
||||
|
||||
var graph = gm._graph(name);
|
||||
var vertexName = graph._vertexCollections()[0].name();
|
||||
var startVertex = db[vertexName].any();
|
||||
|
||||
var aqlQuery =
|
||||
'FOR v, e, p IN 1..2 ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
|
||||
'RETURN p'
|
||||
;
|
||||
|
||||
var cursor = AQL_EXECUTE(aqlQuery);
|
||||
|
||||
var nodesObj = {}, nodesArr = [], edgesObj = {}, edgesArr = [];
|
||||
|
||||
_.each(cursor.json, function(obj) {
|
||||
_.each(obj.edges, function(edge) {
|
||||
if (edge._to && edge._from) {
|
||||
edgesObj[edge._from + edge._to] = {
|
||||
id: edge._id,
|
||||
source: edge._from,
|
||||
target: edge._to
|
||||
};
|
||||
}
|
||||
});
|
||||
var label;
|
||||
_.each(obj.vertices, function(node) {
|
||||
if (node.label) {
|
||||
label = node.label;
|
||||
}
|
||||
else {
|
||||
label = node._id;
|
||||
}
|
||||
|
||||
nodesObj[node._id] = {
|
||||
id: node._id,
|
||||
label: label,
|
||||
size: Math.random(),
|
||||
color: '#2ecc71',
|
||||
x: Math.random(),
|
||||
y: Math.random()
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
//array format for sigma.js
|
||||
_.each(edgesObj, function(node) {
|
||||
edgesArr.push(node);
|
||||
});
|
||||
_.each(nodesObj, function(node) {
|
||||
nodesArr.push(node);
|
||||
});
|
||||
|
||||
res.json({
|
||||
nodes: nodesArr,
|
||||
edges: edgesArr
|
||||
});
|
||||
|
||||
})
|
||||
.summary('Return vertices and edges of a graph.')
|
||||
.description(dd`
|
||||
This function returns vertices and edges for a specific graph.
|
||||
`);
|
||||
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
;(function(undefined) {
|
||||
'use strict';
|
||||
|
||||
if (typeof sigma === 'undefined')
|
||||
throw new Error('sigma is not declared');
|
||||
|
||||
// Initialize package:
|
||||
sigma.utils.pkg('sigma.layout.noverlap');
|
||||
|
||||
/**
|
||||
* Noverlap Layout
|
||||
* ===============================
|
||||
*
|
||||
* Author: @apitts / Andrew Pitts
|
||||
* Algorithm: @jacomyma / Mathieu Jacomy (originally contributed to Gephi and ported to sigma.js under the MIT license by @andpitts with permission)
|
||||
* Acknowledgement: @sheyman / Sébastien Heymann (some inspiration has been taken from other MIT licensed layout algorithms authored by @sheyman)
|
||||
* Version: 0.1
|
||||
*/
|
||||
|
||||
var settings = {
|
||||
speed: 3,
|
||||
scaleNodes: 1.2,
|
||||
nodeMargin: 5.0,
|
||||
gridSize: 20,
|
||||
permittedExpansion: 1.1,
|
||||
rendererIndex: 0,
|
||||
maxIterations: 500
|
||||
};
|
||||
|
||||
var _instance = {};
|
||||
|
||||
/**
|
||||
* Event emitter Object
|
||||
* ------------------
|
||||
*/
|
||||
var _eventEmitter = {};
|
||||
|
||||
/**
|
||||
* Noverlap Object
|
||||
* ------------------
|
||||
*/
|
||||
function Noverlap() {
|
||||
var self = this;
|
||||
|
||||
this.init = function (sigInst, options) {
|
||||
options = options || {};
|
||||
|
||||
// Properties
|
||||
this.sigInst = sigInst;
|
||||
this.config = sigma.utils.extend(options, settings);
|
||||
this.easing = options.easing;
|
||||
this.duration = options.duration;
|
||||
|
||||
if (options.nodes) {
|
||||
this.nodes = options.nodes;
|
||||
delete options.nodes;
|
||||
}
|
||||
|
||||
if (!sigma.plugins || typeof sigma.plugins.animate === 'undefined') {
|
||||
throw new Error('sigma.plugins.animate is not declared');
|
||||
}
|
||||
|
||||
// State
|
||||
this.running = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Single layout iteration.
|
||||
*/
|
||||
this.atomicGo = function () {
|
||||
if (!this.running || this.iterCount < 1) return false;
|
||||
|
||||
var nodes = this.nodes || this.sigInst.graph.nodes(),
|
||||
nodesCount = nodes.length,
|
||||
i,
|
||||
n,
|
||||
n1,
|
||||
n2,
|
||||
xmin = Infinity,
|
||||
xmax = -Infinity,
|
||||
ymin = Infinity,
|
||||
ymax = -Infinity,
|
||||
xwidth,
|
||||
yheight,
|
||||
xcenter,
|
||||
ycenter,
|
||||
grid,
|
||||
row,
|
||||
col,
|
||||
minXBox,
|
||||
maxXBox,
|
||||
minYBox,
|
||||
maxYBox,
|
||||
adjacentNodes,
|
||||
subRow,
|
||||
subCol,
|
||||
nxmin,
|
||||
nxmax,
|
||||
nymin,
|
||||
nymax;
|
||||
|
||||
this.iterCount--;
|
||||
this.running = false;
|
||||
|
||||
for (i=0; i < nodesCount; i++) {
|
||||
n = nodes[i];
|
||||
n.dn.dx = 0;
|
||||
n.dn.dy = 0;
|
||||
|
||||
//Find the min and max for both x and y across all nodes
|
||||
xmin = Math.min(xmin, n.dn_x - (n.dn_size*self.config.scaleNodes + self.config.nodeMargin) );
|
||||
xmax = Math.max(xmax, n.dn_x + (n.dn_size*self.config.scaleNodes + self.config.nodeMargin) );
|
||||
ymin = Math.min(ymin, n.dn_y - (n.dn_size*self.config.scaleNodes + self.config.nodeMargin) );
|
||||
ymax = Math.max(ymax, n.dn_y + (n.dn_size*self.config.scaleNodes + self.config.nodeMargin) );
|
||||
|
||||
}
|
||||
|
||||
xwidth = xmax - xmin;
|
||||
yheight = ymax - ymin;
|
||||
xcenter = (xmin + xmax) / 2;
|
||||
ycenter = (ymin + ymax) / 2;
|
||||
xmin = xcenter - self.config.permittedExpansion*xwidth / 2;
|
||||
xmax = xcenter + self.config.permittedExpansion*xwidth / 2;
|
||||
ymin = ycenter - self.config.permittedExpansion*yheight / 2;
|
||||
ymax = ycenter + self.config.permittedExpansion*yheight / 2;
|
||||
|
||||
grid = {}; //An object of objects where grid[row][col] is an array of node ids representing nodes that fall in that grid. Nodes can fall in more than one grid
|
||||
|
||||
for(row = 0; row < self.config.gridSize; row++) {
|
||||
grid[row] = {};
|
||||
for(col = 0; col < self.config.gridSize; col++) {
|
||||
grid[row][col] = [];
|
||||
}
|
||||
}
|
||||
|
||||
//Place nodes in grid
|
||||
for (i=0; i < nodesCount; i++) {
|
||||
n = nodes[i];
|
||||
|
||||
nxmin = n.dn_x - (n.dn_size*self.config.scaleNodes + self.config.nodeMargin);
|
||||
nxmax = n.dn_x + (n.dn_size*self.config.scaleNodes + self.config.nodeMargin);
|
||||
nymin = n.dn_y - (n.dn_size*self.config.scaleNodes + self.config.nodeMargin);
|
||||
nymax = n.dn_y + (n.dn_size*self.config.scaleNodes + self.config.nodeMargin);
|
||||
|
||||
minXBox = Math.floor(self.config.gridSize* (nxmin - xmin) / (xmax - xmin) );
|
||||
maxXBox = Math.floor(self.config.gridSize* (nxmax - xmin) / (xmax - xmin) );
|
||||
minYBox = Math.floor(self.config.gridSize* (nymin - ymin) / (ymax - ymin) );
|
||||
maxYBox = Math.floor(self.config.gridSize* (nymax - ymin) / (ymax - ymin) );
|
||||
for(col = minXBox; col <= maxXBox; col++) {
|
||||
for(row = minYBox; row <= maxYBox; row++) {
|
||||
grid[row][col].push(n.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
adjacentNodes = {}; //An object that stores the node ids of adjacent nodes (either in same grid box or adjacent grid box) for all nodes
|
||||
|
||||
for(row = 0; row < self.config.gridSize; row++) {
|
||||
for(col = 0; col < self.config.gridSize; col++) {
|
||||
grid[row][col].forEach(function(nodeId) {
|
||||
if(!adjacentNodes[nodeId]) {
|
||||
adjacentNodes[nodeId] = [];
|
||||
}
|
||||
for(subRow = Math.max(0, row - 1); subRow <= Math.min(row + 1, self.config.gridSize - 1); subRow++) {
|
||||
for(subCol = Math.max(0, col - 1); subCol <= Math.min(col + 1, self.config.gridSize - 1); subCol++) {
|
||||
grid[subRow][subCol].forEach(function(subNodeId) {
|
||||
if(subNodeId !== nodeId && adjacentNodes[nodeId].indexOf(subNodeId) === -1) {
|
||||
adjacentNodes[nodeId].push(subNodeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//If two nodes overlap then repulse them
|
||||
for (i=0; i < nodesCount; i++) {
|
||||
n1 = nodes[i];
|
||||
adjacentNodes[n1.id].forEach(function(nodeId) {
|
||||
var n2 = self.sigInst.graph.nodes(nodeId);
|
||||
var xDist = n2.dn_x - n1.dn_x;
|
||||
var yDist = n2.dn_y - n1.dn_y;
|
||||
var dist = Math.sqrt(xDist*xDist + yDist*yDist);
|
||||
var collision = (dist < ((n1.dn_size*self.config.scaleNodes + self.config.nodeMargin) + (n2.dn_size*self.config.scaleNodes + self.config.nodeMargin)));
|
||||
if(collision) {
|
||||
self.running = true;
|
||||
if(dist > 0) {
|
||||
n2.dn.dx += xDist / dist * (1 + n1.dn_size);
|
||||
n2.dn.dy += yDist / dist * (1 + n1.dn_size);
|
||||
} else {
|
||||
n2.dn.dx += xwidth * 0.01 * (0.5 - Math.random());
|
||||
n2.dn.dy += yheight * 0.01 * (0.5 - Math.random());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (i=0; i < nodesCount; i++) {
|
||||
n = nodes[i];
|
||||
if(!n.fixed) {
|
||||
n.dn_x = n.dn_x + n.dn.dx * 0.1 * self.config.speed;
|
||||
n.dn_y = n.dn_y + n.dn.dy * 0.1 * self.config.speed;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.running && this.iterCount < 1) {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
return this.running;
|
||||
};
|
||||
|
||||
this.go = function () {
|
||||
this.iterCount = this.config.maxIterations;
|
||||
|
||||
while (this.running) {
|
||||
this.atomicGo();
|
||||
};
|
||||
|
||||
this.stop();
|
||||
};
|
||||
|
||||
this.start = function() {
|
||||
if (this.running) return;
|
||||
|
||||
var nodes = this.sigInst.graph.nodes();
|
||||
|
||||
var prefix = this.sigInst.renderers[self.config.rendererIndex].options.prefix;
|
||||
|
||||
this.running = true;
|
||||
|
||||
// Init nodes
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].dn_x = nodes[i][prefix + 'x'];
|
||||
nodes[i].dn_y = nodes[i][prefix + 'y'];
|
||||
nodes[i].dn_size = nodes[i][prefix + 'size'];
|
||||
nodes[i].dn = {
|
||||
dx: 0,
|
||||
dy: 0
|
||||
};
|
||||
}
|
||||
_eventEmitter[self.sigInst.id].dispatchEvent('start');
|
||||
this.go();
|
||||
};
|
||||
|
||||
this.stop = function() {
|
||||
var nodes = this.sigInst.graph.nodes();
|
||||
|
||||
this.running = false;
|
||||
|
||||
if (this.easing) {
|
||||
_eventEmitter[self.sigInst.id].dispatchEvent('interpolate');
|
||||
sigma.plugins.animate(
|
||||
self.sigInst,
|
||||
{
|
||||
x: 'dn_x',
|
||||
y: 'dn_y'
|
||||
},
|
||||
{
|
||||
easing: self.easing,
|
||||
onComplete: function() {
|
||||
self.sigInst.refresh();
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].dn = null;
|
||||
nodes[i].dn_x = null;
|
||||
nodes[i].dn_y = null;
|
||||
}
|
||||
_eventEmitter[self.sigInst.id].dispatchEvent('stop');
|
||||
},
|
||||
duration: self.duration
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Apply changes
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].x = nodes[i].dn_x;
|
||||
nodes[i].y = nodes[i].dn_y;
|
||||
}
|
||||
|
||||
this.sigInst.refresh();
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].dn = null;
|
||||
nodes[i].dn_x = null;
|
||||
nodes[i].dn_y = null;
|
||||
}
|
||||
_eventEmitter[self.sigInst.id].dispatchEvent('stop');
|
||||
}
|
||||
};
|
||||
|
||||
this.kill = function() {
|
||||
this.sigInst = null;
|
||||
this.config = null;
|
||||
this.easing = null;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface
|
||||
* ----------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configure the layout algorithm.
|
||||
|
||||
* Recognized options:
|
||||
* **********************
|
||||
* Here is the exhaustive list of every accepted parameter in the settings
|
||||
* object:
|
||||
*
|
||||
* {?number} speed A larger value increases the convergence speed at the cost of precision
|
||||
* {?number} scaleNodes The ratio to scale nodes by - a larger ratio will lead to more space around larger nodes
|
||||
* {?number} nodeMargin A fixed margin to apply around nodes regardless of size
|
||||
* {?number} maxIterations The maximum number of iterations to perform before the layout completes.
|
||||
* {?integer} gridSize The number of rows and columns to use when partioning nodes into a grid for efficient computation
|
||||
* {?number} permittedExpansion A permitted expansion factor to the overall size of the network applied at each iteration
|
||||
* {?integer} rendererIndex The index of the renderer to use for node co-ordinates. Defaults to zero.
|
||||
* {?(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.
|
||||
*
|
||||
*
|
||||
* @param {object} config The optional configuration object.
|
||||
*
|
||||
* @return {sigma.classes.dispatcher} Returns an event emitter.
|
||||
*/
|
||||
sigma.prototype.configNoverlap = function(config) {
|
||||
|
||||
var sigInst = this;
|
||||
|
||||
if (!config) throw new Error('Missing argument: "config"');
|
||||
|
||||
// Create instance if undefined
|
||||
if (!_instance[sigInst.id]) {
|
||||
_instance[sigInst.id] = new Noverlap();
|
||||
|
||||
_eventEmitter[sigInst.id] = {};
|
||||
sigma.classes.dispatcher.extend(_eventEmitter[sigInst.id]);
|
||||
|
||||
// Binding on kill to clear the references
|
||||
sigInst.bind('kill', function() {
|
||||
_instance[sigInst.id].kill();
|
||||
_instance[sigInst.id] = null;
|
||||
_eventEmitter[sigInst.id] = null;
|
||||
});
|
||||
}
|
||||
|
||||
_instance[sigInst.id].init(sigInst, config);
|
||||
|
||||
return _eventEmitter[sigInst.id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Start the layout algorithm. It will use the existing configuration if no
|
||||
* new configuration is passed.
|
||||
|
||||
* Recognized options:
|
||||
* **********************
|
||||
* Here is the exhaustive list of every accepted parameter in the settings
|
||||
* object
|
||||
*
|
||||
* {?number} speed A larger value increases the convergence speed at the cost of precision
|
||||
* {?number} scaleNodes The ratio to scale nodes by - a larger ratio will lead to more space around larger nodes
|
||||
* {?number} nodeMargin A fixed margin to apply around nodes regardless of size
|
||||
* {?number} maxIterations The maximum number of iterations to perform before the layout completes.
|
||||
* {?integer} gridSize The number of rows and columns to use when partioning nodes into a grid for efficient computation
|
||||
* {?number} permittedExpansion A permitted expansion factor to the overall size of the network applied at each iteration
|
||||
* {?integer} rendererIndex The index of the renderer to use for node co-ordinates. Defaults to zero.
|
||||
* {?(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.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {object} config The optional configuration object.
|
||||
*
|
||||
* @return {sigma.classes.dispatcher} Returns an event emitter.
|
||||
*/
|
||||
|
||||
sigma.prototype.startNoverlap = function(config) {
|
||||
|
||||
var sigInst = this;
|
||||
|
||||
if (config) {
|
||||
this.configNoverlap(sigInst, config);
|
||||
}
|
||||
|
||||
_instance[sigInst.id].start();
|
||||
|
||||
return _eventEmitter[sigInst.id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the layout has started and is not completed.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
sigma.prototype.isNoverlapRunning = function() {
|
||||
|
||||
var sigInst = this;
|
||||
|
||||
return !!_instance[sigInst.id] && _instance[sigInst.id].running;
|
||||
};
|
||||
|
||||
}).call(this);
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,204 @@
|
|||
/**
|
||||
* 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);
|
|
@ -41,6 +41,7 @@
|
|||
"node/:name": "node",
|
||||
"logs": "logs",
|
||||
"helpus": "helpUs",
|
||||
"graph2/:name": "graph2",
|
||||
"support": "support"
|
||||
},
|
||||
|
||||
|
@ -683,6 +684,20 @@
|
|||
}
|
||||
this.queryView.render();
|
||||
},
|
||||
|
||||
graph2: function (name, initialized) {
|
||||
this.checkUser();
|
||||
if (!initialized) {
|
||||
this.waitForInit(this.graph2.bind(this), name);
|
||||
return;
|
||||
}
|
||||
if (!this.graphViewer2) {
|
||||
this.graphViewer2 = new window.GraphViewer2({
|
||||
name: name
|
||||
});
|
||||
}
|
||||
this.graphViewer2.render();
|
||||
},
|
||||
|
||||
helpUs: function (initialized) {
|
||||
this.checkUser();
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<script id="graphViewer2.ejs" type="text/template">
|
||||
<div id="graph-container"></div>
|
||||
</script>
|
|
@ -0,0 +1,80 @@
|
|||
/*jshint browser: true */
|
||||
/*jshint unused: false */
|
||||
/*global arangoHelper, sigma, Backbone, templateEngine, $, window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
window.GraphViewer2 = Backbone.View.extend({
|
||||
|
||||
el: "#content",
|
||||
|
||||
template: templateEngine.createTemplate("graphViewer2.ejs"),
|
||||
|
||||
initialize: function(options) {
|
||||
this.name = options.name;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(this.template.render({}));
|
||||
|
||||
//adjust container widht + height
|
||||
$('#graph-container').width($('.centralContent').width());
|
||||
$('#graph-container').height($('.centralRow').height() - 150);
|
||||
|
||||
this.fetchGraph();
|
||||
},
|
||||
|
||||
fetchGraph: function() {
|
||||
var self = this;
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: arangoHelper.databaseUrl("/_admin/aardvark/graph/" + encodeURIComponent(this.name)),
|
||||
contentType: "application/json",
|
||||
success: function(data) {
|
||||
self.renderGraph(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderGraph: function(graph) {
|
||||
var s;
|
||||
|
||||
if (graph.edges.left === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(graph);
|
||||
|
||||
s = new sigma({
|
||||
graph: graph,
|
||||
container: 'graph-container'
|
||||
});
|
||||
|
||||
// Configure the noverlap layout:
|
||||
var noverlapListener = s.configNoverlap({
|
||||
nodeMargin: 0.1,
|
||||
scaleNodes: 1.05,
|
||||
gridSize: 75,
|
||||
easing: 'quadraticInOut', // animation transition function
|
||||
duration: 10000 // animation duration. Long here for the purposes of this example only
|
||||
});
|
||||
|
||||
// Bind the events:
|
||||
noverlapListener.bind('start stop interpolate', function(e) {
|
||||
console.log(e.type);
|
||||
if(e.type === 'start') {
|
||||
console.time('noverlap');
|
||||
}
|
||||
if(e.type === 'interpolate') {
|
||||
console.timeEnd('noverlap');
|
||||
}
|
||||
});
|
||||
|
||||
// Start the layout:
|
||||
s.startNoverlap();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}());
|
|
@ -0,0 +1,3 @@
|
|||
#graph-container {
|
||||
background-color: $c-white;
|
||||
}
|
|
@ -55,6 +55,8 @@
|
|||
@import 'logs';
|
||||
// Graph Viewer
|
||||
@import 'graphViewer';
|
||||
// Graph Viewer 2
|
||||
@import 'graphViewer2';
|
||||
// General Dialogs
|
||||
@import 'dialogs';
|
||||
// Collections
|
||||
|
|
Loading…
Reference in New Issue