1
0
Fork 0

gv [ci skip]

This commit is contained in:
hkernbach 2016-07-13 16:39:56 +02:00
parent 08b892f09f
commit 1929b24901
12 changed files with 620 additions and 112 deletions

View File

@ -65,6 +65,9 @@
"frontend/js/lib/sigma.plugins.fullScreen.js",
"frontend/js/lib/sigma.layout.fruchtermanReingold.js",
"frontend/js/lib/sigma.exporters.svg.js",
"frontend/js/lib/sigma.canvas.edges.labels.curve.js",
"frontend/js/lib/sigma.canvas.edges.labels.curvedArrow.js",
"frontend/js/lib/sigma.canvas.edges.labels.def.js",
"frontend/js/lib/worker.js",
"frontend/js/lib/supervisor.js",
// END SIGMA LIBRARIES

View File

@ -292,10 +292,33 @@ authRouter.get('/graph/:name', function (req, res) {
var graph = gm._graph(name);
var vertexName = graph._vertexCollections()[0].name();
var startVertex = db[vertexName].any();
var startVertex;
var config;
try {
config = req.queryParams;
} catch (e) {
res.throw('bad request', e.message, {cause: e});
}
if (config.nodeStart) {
try {
startVertex = db._document(config.nodeStart);
} catch (e) {
res.throw('bad request', e.message, {cause: e});
}
console.log(startVertex);
if (!startVertex) {
startVertex = db[vertexName].any();
}
} else {
startVertex = db[vertexName].any();
}
var aqlQuery =
'FOR v, e, p IN 1..3 ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
'FOR v, e, p IN 1..' + (config.depth || '2') + ' ANY "' + startVertex._id + '" GRAPH "' + name + '"' +
'RETURN p'
;
@ -307,29 +330,53 @@ authRouter.get('/graph/:name', function (req, res) {
var edgesArr = [];
_.each(cursor.json, function (obj) {
var edgeLabel;
_.each(obj.edges, function (edge) {
if (edge._to && edge._from) {
if (config.edgeLabel) {
// configure edge labels
edgeLabel = edge[config.edgeLabel];
if (edgeLabel) {
edgeLabel = edgeLabel.toString();
}
if (!edgeLabel) {
edgeLabel = 'attribute ' + config.edgeLabel + ' not found';
}
}
edgesObj[edge._from + edge._to] = {
id: edge._id,
source: edge._from,
color: '#cccccc',
label: edgeLabel,
color: config.edgeColor || '#cccccc',
target: edge._to
};
}
});
var label;
var nodeLabel;
var nodeSize;
_.each(obj.vertices, function (node) {
if (node.label) {
label = node.label;
} else {
label = node._id;
if (config.nodeLabel) {
nodeLabel = node[config.nodeLabel];
}
if (!nodeLabel) {
nodeLabel = node._id;
}
if (config.nodeSize) {
nodeSize = node[config.nodeSize];
}
nodesObj[node._id] = {
id: node._id,
label: label,
size: Math.random(),
color: '#2ecc71',
label: nodeLabel,
size: nodeSize || Math.random(),
color: config.nodeColor || '#2ecc71',
x: Math.random(),
y: Math.random()
};

View File

@ -0,0 +1,112 @@
;(function(undefined) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
// Initialize packages:
sigma.utils.pkg('sigma.canvas.edges.labels');
/**
* This label renderer will just display the label on the curve of the edge.
* The label is rendered at half distance of the edge extremities, and is
* always oriented from left to right on the top side of the curve.
*
* @param {object} edge The edge object.
* @param {object} source node The edge source node.
* @param {object} target node The edge target node.
* @param {CanvasRenderingContext2D} context The canvas context.
* @param {configurable} settings The settings function.
*/
sigma.canvas.edges.labels.curve =
function(edge, source, target, context, settings) {
if (typeof edge.label !== 'string')
return;
var prefix = settings('prefix') || '',
size = edge[prefix + 'size'] || 1;
if (size < settings('edgeLabelThreshold'))
return;
var fontSize,
sSize = source[prefix + 'size'],
sX = source[prefix + 'x'],
sY = source[prefix + 'y'],
tX = target[prefix + 'x'],
tY = target[prefix + 'y'],
dX = tX - sX,
dY = tY - sY,
sign = (sX < tX) ? 1 : -1,
cp = {},
c,
angle,
t = 0.5; //length of the curve
if (source.id === target.id) {
cp = sigma.utils.getSelfLoopControlPoints(sX, sY, sSize);
c = sigma.utils.getPointOnBezierCurve(
t, sX, sY, tX, tY, cp.x1, cp.y1, cp.x2, cp.y2
);
angle = Math.atan2(1, 1); // 45°
} else {
cp = sigma.utils.getQuadraticControlPoint(sX, sY, tX, tY);
c = sigma.utils.getPointOnQuadraticCurve(t, sX, sY, tX, tY, cp.x, cp.y);
angle = Math.atan2(dY * sign, dX * sign);
}
// The font size is sublineraly proportional to the edge size, in order to
// avoid very large labels on screen.
// This is achieved by f(x) = x * x^(-1/ a), where 'x' is the size and 'a'
// is the edgeLabelSizePowRatio. Notice that f(1) = 1.
// The final form is:
// f'(x) = b * x * x^(-1 / a), thus f'(1) = b. Application:
// fontSize = defaultEdgeLabelSize if edgeLabelSizePowRatio = 1
fontSize = (settings('edgeLabelSize') === 'fixed') ?
settings('defaultEdgeLabelSize') :
settings('defaultEdgeLabelSize') *
size *
Math.pow(size, -1 / settings('edgeLabelSizePowRatio'));
context.save();
if (edge.active) {
context.font = [
settings('activeFontStyle'),
fontSize + 'px',
settings('activeFont') || settings('font')
].join(' ');
context.fillStyle =
settings('edgeActiveColor') === 'edge' ?
(edge.active_color || settings('defaultEdgeActiveColor')) :
settings('defaultEdgeLabelActiveColor');
}
else {
context.font = [
settings('fontStyle'),
fontSize + 'px',
settings('font')
].join(' ');
context.fillStyle =
(settings('edgeLabelColor') === 'edge') ?
(edge.color || settings('defaultEdgeColor')) :
settings('defaultEdgeLabelColor');
}
context.textAlign = 'center';
context.textBaseline = 'alphabetic';
context.translate(c.x, c.y);
context.rotate(angle);
context.fillText(
edge.label,
0,
(-size / 2) - 3
);
context.restore();
};
}).call(this);

View File

@ -0,0 +1,25 @@
;(function(undefined) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
// Initialize packages:
sigma.utils.pkg('sigma.canvas.edges.labels');
/**
* This label renderer will just display the label on the curve of the edge.
* The label is rendered at half distance of the edge extremities, and is
* always oriented from left to right on the top side of the curve.
*
* @param {object} edge The edge object.
* @param {object} source node The edge source node.
* @param {object} target node The edge target node.
* @param {CanvasRenderingContext2D} context The canvas context.
* @param {configurable} settings The settings function.
*/
sigma.canvas.edges.labels.curvedArrow =
function(edge, source, target, context, settings) {
sigma.canvas.edges.labels.curve(edge, source, target, context, settings);
};
}).call(this);

View File

@ -0,0 +1,96 @@
;(function(undefined) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
// Initialize packages:
sigma.utils.pkg('sigma.canvas.edges.labels');
/**
* This label renderer will just display the label on the line of the edge.
* The label is rendered at half distance of the edge extremities, and is
* always oriented from left to right on the top side of the line.
*
* @param {object} edge The edge object.
* @param {object} source node The edge source node.
* @param {object} target node The edge target node.
* @param {CanvasRenderingContext2D} context The canvas context.
* @param {configurable} settings The settings function.
*/
sigma.canvas.edges.labels.def =
function(edge, source, target, context, settings) {
if (typeof edge.label !== 'string' || source == target)
return;
var prefix = settings('prefix') || '',
size = edge[prefix + 'size'] || 1;
if (size < settings('edgeLabelThreshold'))
return;
if (0 === settings('edgeLabelSizePowRatio'))
throw '"edgeLabelSizePowRatio" must not be 0.';
var fontSize,
x = (source[prefix + 'x'] + target[prefix + 'x']) / 2,
y = (source[prefix + 'y'] + target[prefix + 'y']) / 2,
dX = target[prefix + 'x'] - source[prefix + 'x'],
dY = target[prefix + 'y'] - source[prefix + 'y'],
sign = (source[prefix + 'x'] < target[prefix + 'x']) ? 1 : -1,
angle = Math.atan2(dY * sign, dX * sign);
// The font size is sublineraly proportional to the edge size, in order to
// avoid very large labels on screen.
// This is achieved by f(x) = x * x^(-1/ a), where 'x' is the size and 'a'
// is the edgeLabelSizePowRatio. Notice that f(1) = 1.
// The final form is:
// f'(x) = b * x * x^(-1 / a), thus f'(1) = b. Application:
// fontSize = defaultEdgeLabelSize if edgeLabelSizePowRatio = 1
fontSize = (settings('edgeLabelSize') === 'fixed') ?
settings('defaultEdgeLabelSize') :
settings('defaultEdgeLabelSize') *
size *
Math.pow(size, -1 / settings('edgeLabelSizePowRatio'));
context.save();
if (edge.active) {
context.font = [
settings('activeFontStyle'),
fontSize + 'px',
settings('activeFont') || settings('font')
].join(' ');
context.fillStyle =
settings('edgeActiveColor') === 'edge' ?
(edge.active_color || settings('defaultEdgeActiveColor')) :
settings('defaultEdgeLabelActiveColor');
}
else {
context.font = [
settings('fontStyle'),
fontSize + 'px',
settings('font')
].join(' ');
context.fillStyle =
(settings('edgeLabelColor') === 'edge') ?
(edge.color || settings('defaultEdgeColor')) :
settings('defaultEdgeLabelColor');
}
context.textAlign = 'center';
context.textBaseline = 'alphabetic';
context.translate(x, y);
context.rotate(angle);
context.fillText(
edge.label,
0,
(-size / 2) - 3
);
context.restore();
};
}).call(this);

View File

@ -42,7 +42,7 @@ window.UserConfig = Backbone.Model.extend({
}
},
error: function () {
arangoHelper.arangoNotification('User configuration', 'Could not update user configuration for key: ' + keyName);
arangoHelper.arangoError('User configuration', 'Could not update user configuration for key: ' + keyName);
}
});
},
@ -61,7 +61,7 @@ window.UserConfig = Backbone.Model.extend({
callback(keyValue);
},
error: function () {
arangoHelper.arangoNotification('User configuration', 'Could not fetch user configuration for key: ' + keyName);
arangoHelper.arangoError('User configuration', 'Could not fetch user configuration for key: ' + keyName);
}
});
}

View File

@ -671,6 +671,9 @@
this.waitForInit(this.graph2.bind(this), name);
return;
}
if (this.graphViewer2) {
this.graphViewer2.remove();
}
this.graphViewer2 = new window.GraphViewer2({
name: name,
userConfig: this.userConfig
@ -684,6 +687,9 @@
this.waitForInit(this.graph2settings.bind(this), name);
return;
}
if (this.graphSettingsView) {
this.graphSettingsView.remove();
}
this.graphSettingsView = new window.GraphSettingsView({
name: name,
userConfig: this.userConfig

View File

@ -7,7 +7,7 @@
<% return name.charAt(0).toUpperCase() + string.slice(1);%>
<% }; %>
<div id="shardsContent" class="innerContent">
<div id="graphSettingsView" class="innerContent">
<div class="pure-g" style="margin-top: -15px">
@ -21,47 +21,51 @@
</div>
</div>
<div class="pure-g pure-table pure-table-header pure-title">
<div class="pure-table-row">
<div class="<%= genClass %> left">Name</div>
<div class="<%= genClass %> left">Property</div>
</div>
</div>
<div class="pure-g pure-table pure-table-body">
<% _.each(specific, function(val, key) { %>
<div class="<%= genClass %> left"><%=val.name%></div>
<div class="<%= genClass %> left">
<% if (val.type === 'divider') { %>
<div class="heading <%= genClass %> left"><%=val.name%></div>
<div class="<%= genClass %> left"></div>
<% } else { %>
<div class="<%= genClass %> left"><%=val.name%></div>
<div class="<%= genClass %> left">
<% var VALUE; %>
<% if (val.value) { %>
<% VALUE = val.value %>
<% } else { %>
<% VALUE = val.default %>
<% } %>
<% var VALUE; %>
<% if (val.value) { %>
<% VALUE = val.value %>
<% } else { %>
<% VALUE = val.default %>
<% } %>
<% if (val.type === 'string') { %>
<input type="text" value="<%=VALUE%>" placeholder=""></input>
<% } %>
<% if (val.type === 'string') { %>
<input id="g_<%=key%>" type="text" placeholder="string"></input>
<% } %>
<% if (val.type === 'color') { %>
<input type='color' name='color' value="<%=VALUE%>"/>
<% } %>
<% if (val.type === 'number') { %>
<input id="g_<%=key%>" type="text" placeholder="number"></input>
<% } %>
<% if (val.type === 'select') { %>
<div class="<%= genClass %> left">
<select>
<% _.each(val, function(option, optKey) { %>
<% if (option.name) { %>
<option> <%=option.name%> </option>
<% } %>
<% }); %>
</select>
</div>
<% } %>
<% if (val.type === 'color') { %>
<input id="g_<%=key%>" type='color' name='color' value="<%=VALUE%>"/>
<% } %>
</div>
<% if (val.type === 'select') { %>
<div class="<%= genClass %> left">
<select id="g_<%=key%>">
<% _.each(val, function(option, optKey) { %>
<% if (option.name) { %>
<option value="<%=option.val%>"> <%=option.name%> </option>
<% } %>
<% }); %>
</select>
</div>
<% } %>
</div>
<% } %>
<% }); %>
</div>
@ -77,16 +81,13 @@
</div>
</div>
<div class="pure-g pure-table pure-table-header pure-title">
<div class="pure-table-row">
<div class="<%= genClass %> left">Name</div>
<div class="<%= genClass %> left">Property</div>
</div>
</div>
<div class="pure-g pure-table pure-table-body">
<% _.each(general, function(val, key) { %>
<% if (val.type === 'divider') { %>
<div class="heading <%= genClass %> left"><%=val.name%></div>
<div class="<%= genClass %> left"></div>
<% } else { %>
<div class="<%= genClass %> left"><%=val.name%></div>
<% if (val.type === 'select') { %>
@ -101,9 +102,14 @@
</div>
<% } %>
<% if (val.type === 'numeric') { %>
<% if (val.type === 'string') { %>
<input id="g_<%=key%>" type="text" placeholder="string"></input>
<% } %>
<% if (val.type === 'number') { %>
<input id="g_<%=key%>" type="text" id="<%=val %>" value="<%=val.value %>" placeholder=""></input>
<% } %>
<% } %>
<% }); %>
</div>

View File

@ -7,7 +7,23 @@
window.GraphSettingsView = Backbone.View.extend({
el: '#content',
remove: function () {
this.$el.empty().off(); /* off to unbind the events */
this.stopListening();
return this;
},
general: {
'graph': {
type: 'divider',
name: 'Graph'
},
'nodeStart': {
type: 'string',
name: 'Starting node',
desc: 'A valid node id. If empty, a random node will be chosen.',
value: 2
},
'layout': {
type: 'select',
name: 'Layout algorithm',
@ -37,58 +53,73 @@
}
},
'depth': {
type: 'numeric',
type: 'number',
name: 'Search depth',
value: 2
}
},
specific: {
'nodes': {
type: 'divider',
name: 'Nodes'
},
'nodeLabel': {
type: 'string',
name: 'Node label',
name: 'Label',
desc: 'Default node color. RGB or HEX value.',
default: '_key'
},
'nodeColor': {
type: 'color',
name: 'Node color',
name: 'Color',
desc: 'Default node color. RGB or HEX value.',
default: '#2ecc71'
},
'nodeSize': {
type: 'string',
name: 'Node size',
desc: 'Default node size. Numeric value > 0.',
value: undefined
name: 'Sizing attribute',
desc: 'Default node size. Numeric value > 0.'
},
'edges': {
type: 'divider',
name: 'Edges'
},
'edgeLabel': {
type: 'string',
name: 'Edge label',
desc: 'Default edge label.',
value: undefined
name: 'Label',
desc: 'Default edge label.'
},
'edgeColor': {
type: 'color',
name: 'Edge color',
name: 'Color',
desc: 'Default edge color. RGB or HEX value.',
default: '#cccccc'
},
'edgeSize': {
type: 'string',
name: 'Edge thickness',
desc: 'Default edge thickness. Numeric value > 0.',
value: undefined
type: 'number',
name: 'Sizing',
desc: 'Default edge thickness. Numeric value > 0.'
},
'edgeType': {
type: 'select',
name: 'Edge type',
name: 'Type',
desc: 'The type of the edge',
canvas: {
name: 'Straight'
line: {
name: 'Line',
val: 'line'
},
webgl: {
name: 'Curved'
curve: {
name: 'Curve',
val: 'curve'
},
arrow: {
name: 'Arrow',
val: 'arrow'
},
curvedArrow: {
name: 'Curved Arrow',
val: 'curvedArrow'
}
}
},
@ -112,7 +143,6 @@
this.userConfig.fetch({
success: function (data) {
self.graphConfig = data.toJSON().graphs[combinedName];
if (render) {
self.continueRender();
}
@ -127,7 +157,15 @@
config[combinedName] = {
layout: $('#g_layout').val(),
renderer: $('#g_renderer').val(),
depth: $('#g_depth').val()
depth: $('#g_depth').val(),
nodeColor: $('#g_nodeColor').val(),
edgeColor: $('#g_edgeColor').val(),
nodeLabel: $('#g_nodeLabel').val(),
edgeLabel: $('#g_edgeLabel').val(),
edgeType: $('#g_edgeType').val(),
nodeSize: $('#g_nodeSize').val(),
edgeSize: $('#g_edgeSize').val(),
nodeStart: $('#g_nodeStart').val()
};
var callback = function () {

View File

@ -1,12 +1,18 @@
/* jshint browser: true */
/* jshint unused: false */
/* global arangoHelper, slicePath, icon, wheelnav, document, sigma, Backbone, templateEngine, $, window*/
/* global arangoHelper, _, slicePath, icon, wheelnav, document, sigma, Backbone, templateEngine, $, window*/
(function () {
'use strict';
window.GraphViewer2 = Backbone.View.extend({
el: '#content',
remove: function () {
this.$el.empty().off(); /* off to unbind the events */
this.stopListening();
return this;
},
template: templateEngine.createTemplate('graphViewer2.ejs'),
initialize: function (options) {
@ -64,30 +70,104 @@
$('#content').append(
'<div id="calculatingGraph" style="position: absolute; left: 25px; top: 130px;">' +
'<i class="fa fa-circle-o-notch fa-spin" style="margin-right: 10px;"></i>' +
'Calculating layout. Please wait ... </div>'
'<span id="calcText">Fetching graph data. Please wait ... </span></div>'
);
var fetchGraph = function () {
var continueFetchGraph = function () {
var ajaxData = {};
if (this.graphConfig) {
ajaxData = _.clone(this.graphConfig);
// remove not needed params
delete ajaxData.layout;
delete ajaxData.edgeType;
delete ajaxData.renderer;
}
this.setupSigma();
$.ajax({
type: 'GET',
url: arangoHelper.databaseUrl('/_admin/aardvark/graph/' + encodeURIComponent(this.name)),
contentType: 'application/json',
data: ajaxData,
success: function (data) {
$('#calcText').html('Calculating layout. Please wait ... ');
arangoHelper.buildGraphSubNav(self.name, 'Content');
self.renderGraph(data);
},
error: function () {
error: function (e) {
console.log(e);
try {
arangoHelper.arangoError('Graph', e.responseJSON.exception);
} catch (ignore) {}
$('#calculatingGraph').html('Failed to fetch graph information.');
}
});
}.bind(this);
// TODO LOAD GRAPH SETTINGS
this.getGraphSettings(fetchGraph);
// load graph configuration
this.getGraphSettings(continueFetchGraph);
},
clearOldContextMenu: function () {
setupSigma: function () {
if (this.graphConfig) {
if (this.graphConfig.edgeLabel) {
// Initialize package:
sigma.utils.pkg('sigma.settings');
var settings = {
defaultEdgeLabelColor: '#000',
defaultEdgeLabelActiveColor: '#000',
defaultEdgeLabelSize: 10,
edgeLabelSize: 'fixed',
edgeLabelSizePowRatio: 1,
edgeLabelThreshold: 1
};
// Export the previously designed settings:
sigma.settings = sigma.utils.extend(sigma.settings || {}, settings);
// Override default settings:
sigma.settings.drawEdgeLabels = true;
}
}
},
contextState: {
createEdge: false,
_from: false,
_to: false,
fromX: false,
fromY: false
},
clearOldContextMenu: function (states) {
var self = this;
// clear dom
$('#nodeContextMenu').remove();
var string = '<div id="nodeContextMenu" class="nodeContextMenu"></div>';
$('#graph-container').append(string);
// clear state
if (states) {
_.each(this.contextState, function (val, key) {
self.contextState[key] = false;
});
}
// clear info div
},
createContextMenu: function (e) {
var x = e.data.captor.clientX;
var y = e.data.captor.clientX;
console.log('Context menu');
console.log(x);
console.log(y);
this.clearOldContextMenu();
},
createNodeContextMenu: function (nodeId, e) {
@ -97,8 +177,6 @@
var y = e.data.node['renderer1:y'];
this.clearOldContextMenu();
var string = '<div id="nodeContextMenu" class="nodeContextMenu"></div>';
$('#graph-container').append(string);
var generateMenu = function (e, nodeId) {
var hotaru = ['#364C4A', '#497C7F', '#92C5C0', '#858168', '#CCBCA5'];
@ -110,35 +188,49 @@
wheel.wheelRadius = 50;
wheel.clockwise = false;
wheel.colors = hotaru;
wheel.multiSelect = true;
wheel.clickModeRotate = false;
wheel.slicePathFunction = slicePath().DonutSlice;
wheel.createWheel([icon.edit, icon.trash, icon.smallgear, icon.smallgear]);
wheel.createWheel([icon.edit, icon.trash, icon.arrowleft2, icon.connect]);
wheel.navItems[0].selected = false;
wheel.navItems[0].hovered = false;
// add menu events
// function 0: edit
wheel.navItems[0].navigateFunction = function () {
wheel.navItems[0].navigateFunction = function (e) {
self.clearOldContextMenu();
self.editNode(nodeId);
};
// function 1:
wheel.navItems[1].navigateFunction = function () {
// function 1: delete
wheel.navItems[1].navigateFunction = function (e) {
self.clearOldContextMenu();
self.editNode(nodeId);
self.deleteNode(nodeId);
};
// function 2:
wheel.navItems[2].navigateFunction = function () {
// function 2: mark as start node
wheel.navItems[2].navigateFunction = function (e) {
self.clearOldContextMenu();
self.editNode(nodeId);
self.setStartNode(nodeId);
};
// function 3: delete
wheel.navItems[3].navigateFunction = function () {
// function 3: create edge
wheel.navItems[3].navigateFunction = function (e) {
self.contextState.createEdge = true;
self.contextState._from = nodeId;
self.contextState.fromX = x;
self.contextState.fromY = y;
var c = document.getElementsByClassName('sigma-mouse')[0];
c.addEventListener('mousemove', self.drawLine.bind(this), false);
self.clearOldContextMenu();
self.editNode(nodeId);
};
// deselect active default entry
wheel.navItems[0].selected = false;
wheel.navItems[0].hovered = false;
};
$('#nodeContextMenu').css('left', x + 115);
@ -149,6 +241,31 @@
generateMenu(e, nodeId);
},
clearMouseCanvas: function () {
var c = document.getElementsByClassName('sigma-mouse')[0];
var ctx = c.getContext('2d');
ctx.clearRect(0, 0, $(c).width(), $(c).height());
},
drawLine: function (e) {
var context = window.App.graphViewer2.contextState;
if (context.createEdge) {
var fromX = context.fromX;
var fromY = context.fromY;
var toX = e.offsetX;
var toY = e.offsetY;
var c = document.getElementsByClassName('sigma-mouse')[0];
var ctx = c.getContext('2d');
ctx.clearRect(0, 0, $(c).width(), $(c).height());
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.stroke();
}
},
getGraphSettings: function (callback) {
var self = this;
var combinedName = window.App.currentDB.toJSON().name + '_' + this.name;
@ -158,7 +275,7 @@
self.graphConfig = data.toJSON().graphs[combinedName];
if (callback) {
callback();
callback(self.graphConfig);
}
}
});
@ -184,8 +301,6 @@
var renderer = 'canvas';
if (this.graphConfig) {
console.log(this.graphConfig);
if (this.graphConfig.layout) {
algorithm = this.graphConfig.layout;
}
@ -201,20 +316,28 @@
var settings = {
doubleClickEnabled: false,
minEdgeSize: 0.5,
minNodeSize: 3.5,
minEdgeSize: 1,
maxEdgeSize: 4,
enableEdgeHovering: true,
// edgeHoverColor: 'edge',
// defaultEdgeHoverColor: '#000',
// defaultEdgeType: 'curve',
edgeHoverSizeRatio: 1,
edgeHoverColor: '#000',
defaultEdgeHoverColor: '#000',
defaultEdgeType: 'line',
edgeHoverSizeRatio: 2,
edgeHoverExtremities: true
};
if (this.graphConfig) {
if (this.graphConfig.edgeType) {
settings.defaultEdgeType = this.graphConfig.edgeType;
}
}
// adjust display settings for big graphs
if (graph.nodes.length > 500) {
// show node label if size is 20
settings.labelThreshold = 20;
// show node label if size is 15
settings.labelThreshold = 15;
settings.hideEdgesOnMove = true;
}
// adjust display settings for webgl renderer
@ -271,7 +394,33 @@
e.originalColor = e.color;
});
if (renderer !== 'webgl') {
// for canvas renderer allow graph editing
if (renderer === 'canvas') {
s.bind('rightClickStage', function (e) {
self.createContextMenu(e);
self.clearMouseCanvas();
});
s.bind('clickNode', function (e) {
if (self.contextState.createEdge === true) {
// create the edge
self.contextState._to = e.data.node.id;
self.currentGraph.graph.addEdge({
source: self.contextState._from,
target: self.contextState._to,
id: Math.random(),
color: self.graphConfig.edgeColor
});
// rerender graph
self.currentGraph.refresh();
// then clear states
self.clearOldContextMenu(true);
}
});
s.bind('rightClickNode', function (e) {
var nodeId = e.data.node.id;
self.createNodeContextMenu(nodeId, e);
@ -314,7 +463,8 @@
});
s.bind('clickStage', function () {
self.clearOldContextMenu();
self.clearOldContextMenu(true);
self.clearMouseCanvas();
});
}
@ -327,11 +477,18 @@
} else if (algorithm === 'force') {
s.startForceAtlas2({worker: true, barnesHutOptimize: false});
var duration = 3000;
if (graph.nodes.length > 2500) {
duration = 5000;
} else if (graph.nodes.length < 50) {
duration = 500;
}
window.setTimeout(function () {
s.stopForceAtlas2();
dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
console.log('stopped force');
}, 3000);
}, duration);
} else if (algorithm === 'fruchtermann') {
// Start the Fruchterman-Reingold algorithm:
sigma.layouts.fruchtermanReingold.start(s);

View File

@ -45,8 +45,19 @@
position: fixed;
svg {
path {
font-size: 2pt !important;
#wheelnav-nodeContextMenu-title-0 {
transform: translate(24px, 14px) scale(.7) !important;
}
#wheelnav-nodeContextMenu-title-0,
#wheelnav-nodeContextMenu-title-1,
#wheelnav-nodeContextMenu-title-2,
#wheelnav-nodeContextMenu-title-3 {
fill: $c-white;
&:hover {
fill: $c-positive;
}
}
}
}

View File

@ -9,6 +9,13 @@
.dataTables_empty {
padding-left: 10px;
}
.heading {
font-weight: 600;
height: 40px;
padding-bottom: 10px;
padding-top: 10px;
}
}
.pure-table {