react-sigma-conglei
Version:
Lightweight but powerful library for drawing network graphs built on top of dunnock/react-sigma
1,707 lines (1,484 loc) • 63.4 kB
JavaScript
var Sigma =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 84);
/******/ })
/************************************************************************/
/******/ ({
/***/ 62:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function(global) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
// Initialize packages:
sigma.utils.pkg('sigma.renderers');
// Check if WebGL is enabled:
var canvas,
webgl = !!global.WebGLRenderingContext;
if (webgl) {
canvas = document.createElement('canvas');
try {
webgl = !!(
canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl')
);
} catch (e) {
webgl = false;
}
}
// Copy the good renderer:
sigma.renderers.def = webgl ?
sigma.renderers.webgl :
sigma.renderers.canvas;
})(this);
}.call(window));
/***/ }),
/***/ 64:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function(undefined) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
// Initialize packages:
sigma.utils.pkg('sigma.renderers');
/**
* This function is the constructor of the canvas sigma's renderer.
*
* @param {sigma.classes.graph} graph The graph to render.
* @param {sigma.classes.camera} camera The camera.
* @param {configurable} settings The sigma instance settings
* function.
* @param {object} object The options object.
* @return {sigma.renderers.canvas} The renderer instance.
*/
sigma.renderers.webgl = function(graph, camera, settings, options) {
if (typeof options !== 'object')
throw 'sigma.renderers.webgl: Wrong arguments.';
if (!(options.container instanceof HTMLElement))
throw 'Container not found.';
var k,
i,
l,
a,
fn,
_self = this;
sigma.classes.dispatcher.extend(this);
// Conrad related attributes:
this.jobs = {};
Object.defineProperty(this, 'conradId', {
value: sigma.utils.id()
});
// Initialize main attributes:
this.graph = graph;
this.camera = camera;
this.contexts = {};
this.domElements = {};
this.options = options;
this.container = this.options.container;
this.settings = (
typeof options.settings === 'object' &&
options.settings
) ?
settings.embedObjects(options.settings) :
settings;
// Find the prefix:
this.options.prefix = this.camera.readPrefix;
// Initialize programs hash
Object.defineProperty(this, 'nodePrograms', {
value: {}
});
Object.defineProperty(this, 'edgePrograms', {
value: {}
});
Object.defineProperty(this, 'nodeFloatArrays', {
value: {}
});
Object.defineProperty(this, 'edgeFloatArrays', {
value: {}
});
Object.defineProperty(this, 'edgeIndicesArrays', {
value: {}
});
// Initialize the DOM elements:
if (this.settings(options, 'batchEdgesDrawing')) {
this.initDOM('canvas', 'edges', true);
this.initDOM('canvas', 'nodes', true);
} else {
this.initDOM('canvas', 'scene', true);
this.contexts.nodes = this.contexts.scene;
this.contexts.edges = this.contexts.scene;
}
this.initDOM('canvas', 'labels');
this.initDOM('canvas', 'mouse');
this.contexts.hover = this.contexts.mouse;
// Initialize captors:
this.captors = [];
a = this.options.captors || [sigma.captors.mouse, sigma.captors.touch];
for (i = 0, l = a.length; i < l; i++) {
fn = typeof a[i] === 'function' ? a[i] : sigma.captors[a[i]];
this.captors.push(
new fn(
this.domElements.mouse,
this.camera,
this.settings
)
);
}
// Deal with sigma events:
sigma.misc.bindEvents.call(this, this.camera.prefix);
sigma.misc.drawHovers.call(this, this.camera.prefix);
this.resize();
};
/**
* This method will generate the nodes and edges float arrays. This step is
* separated from the "render" method, because to keep WebGL efficient, since
* all the camera and middlewares are modelised as matrices and they do not
* require the float arrays to be regenerated.
*
* Basically, when the user moves the camera or applies some specific linear
* transformations, this process step will be skipped, and the "render"
* method will efficiently refresh the rendering.
*
* And when the user modifies the graph colors or positions (applying a new
* layout or filtering the colors, for instance), this "process" step will be
* required to regenerate the float arrays.
*
* @return {sigma.renderers.webgl} Returns the instance itself.
*/
sigma.renderers.webgl.prototype.process = function() {
var a,
i,
l,
k,
type,
renderer,
graph = this.graph,
options = sigma.utils.extend(options, this.options),
defaultEdgeType = this.settings(options, 'defaultEdgeType'),
defaultNodeType = this.settings(options, 'defaultNodeType');
// Empty float arrays:
for (k in this.nodeFloatArrays)
delete this.nodeFloatArrays[k];
for (k in this.edgeFloatArrays)
delete this.edgeFloatArrays[k];
for (k in this.edgeIndicesArrays)
delete this.edgeIndicesArrays[k];
// Sort edges and nodes per types:
for (a = graph.edges(), i = 0, l = a.length; i < l; i++) {
type = a[i].type || defaultEdgeType;
k = (type && sigma.webgl.edges[type]) ? type : 'def';
if (!this.edgeFloatArrays[k])
this.edgeFloatArrays[k] = {
edges: []
};
this.edgeFloatArrays[k].edges.push(a[i]);
}
for (a = graph.nodes(), i = 0, l = a.length; i < l; i++) {
type = a[i].type || defaultNodeType;
k = (type && sigma.webgl.nodes[type]) ? type : 'def';
if (!this.nodeFloatArrays[k])
this.nodeFloatArrays[k] = {
nodes: []
};
this.nodeFloatArrays[k].nodes.push(a[i]);
}
// Push edges:
for (k in this.edgeFloatArrays) {
renderer = sigma.webgl.edges[k];
a = this.edgeFloatArrays[k].edges;
// Creating the necessary arrays
this.edgeFloatArrays[k].array = new Float32Array(
a.length * renderer.POINTS * renderer.ATTRIBUTES
);
for (i = 0, l = a.length; i < l; i++) {
// Just check that the edge and both its extremities are visible:
if (
!a[i].hidden &&
!graph.nodes(a[i].source).hidden &&
!graph.nodes(a[i].target).hidden
)
renderer.addEdge(
a[i],
graph.nodes(a[i].source),
graph.nodes(a[i].target),
this.edgeFloatArrays[k].array,
i * renderer.POINTS * renderer.ATTRIBUTES,
options.prefix,
this.settings
);
}
if (typeof renderer.computeIndices === 'function')
this.edgeIndicesArrays[k] = renderer.computeIndices(
this.edgeFloatArrays[k].array
);
}
// Push nodes:
for (k in this.nodeFloatArrays) {
renderer = sigma.webgl.nodes[k];
a = this.nodeFloatArrays[k].nodes;
// Creating the necessary arrays
this.nodeFloatArrays[k].array = new Float32Array(
a.length * renderer.POINTS * renderer.ATTRIBUTES
);
for (i = 0, l = a.length; i < l; i++) {
if (!this.nodeFloatArrays[k].array)
this.nodeFloatArrays[k].array = new Float32Array(
a.length * renderer.POINTS * renderer.ATTRIBUTES
);
// Just check that the edge and both its extremities are visible:
if (
!a[i].hidden
)
renderer.addNode(
a[i],
this.nodeFloatArrays[k].array,
i * renderer.POINTS * renderer.ATTRIBUTES,
options.prefix,
this.settings
);
}
}
return this;
};
/**
* This method renders the graph. It basically calls each program (and
* generate them if they do not exist yet) to render nodes and edges, batched
* per renderer.
*
* As in the canvas renderer, it is possible to display edges, nodes and / or
* labels in batches, to make the whole thing way more scalable.
*
* @param {?object} params Eventually an object of options.
* @return {sigma.renderers.webgl} Returns the instance itself.
*/
sigma.renderers.webgl.prototype.render = function(params) {
var a,
i,
l,
k,
o,
program,
renderer,
self = this,
graph = this.graph,
nodesGl = this.contexts.nodes,
edgesGl = this.contexts.edges,
matrix = this.camera.getMatrix(),
options = sigma.utils.extend(params, this.options),
drawLabels = this.settings(options, 'drawLabels'),
drawEdges = this.settings(options, 'drawEdges'),
drawNodes = this.settings(options, 'drawNodes');
// Call the resize function:
this.resize(false);
// Check the 'hideEdgesOnMove' setting:
if (this.settings(options, 'hideEdgesOnMove'))
if (this.camera.isAnimated || this.camera.isMoving)
drawEdges = false;
// Clear canvases:
this.clear();
// Translate matrix to [width/2, height/2]:
matrix = sigma.utils.matrices.multiply(
matrix,
sigma.utils.matrices.translation(this.width / 2, this.height / 2)
);
// Kill running jobs:
for (k in this.jobs)
if (conrad.hasJob(k))
conrad.killJob(k);
if (drawEdges) {
if (this.settings(options, 'batchEdgesDrawing'))
(function() {
var a,
k,
i,
id,
job,
arr,
end,
start,
indices,
renderer,
batchSize,
currentProgram;
id = 'edges_' + this.conradId;
batchSize = this.settings(options, 'webglEdgesBatchSize');
a = Object.keys(this.edgeFloatArrays);
if (!a.length)
return;
i = 0;
renderer = sigma.webgl.edges[a[i]];
arr = this.edgeFloatArrays[a[i]].array;
indices = this.edgeIndicesArrays[a[i]];
start = 0;
end = Math.min(
start + batchSize * renderer.POINTS,
arr.length / renderer.ATTRIBUTES
);
job = function() {
// Check program:
if (!this.edgePrograms[a[i]])
this.edgePrograms[a[i]] = renderer.initProgram(edgesGl);
if (start < end) {
edgesGl.useProgram(this.edgePrograms[a[i]]);
renderer.render(
edgesGl,
this.edgePrograms[a[i]],
arr,
{
settings: this.settings,
matrix: matrix,
width: this.width,
height: this.height,
ratio: this.camera.ratio,
scalingRatio: this.settings(
options,
'webglOversamplingRatio'
),
start: start,
count: end - start,
indicesData: indices
}
);
}
// Catch job's end:
if (
end >= arr.length / renderer.ATTRIBUTES &&
i === a.length - 1
) {
delete this.jobs[id];
return false;
}
if (end >= arr.length / renderer.ATTRIBUTES) {
i++;
arr = this.edgeFloatArrays[a[i]].array;
renderer = sigma.webgl.edges[a[i]];
start = 0;
end = Math.min(
start + batchSize * renderer.POINTS,
arr.length / renderer.ATTRIBUTES
);
} else {
start = end;
end = Math.min(
start + batchSize * renderer.POINTS,
arr.length / renderer.ATTRIBUTES
);
}
return true;
};
this.jobs[id] = job;
conrad.addJob(id, job.bind(this));
}).call(this);
else {
for (k in this.edgeFloatArrays) {
renderer = sigma.webgl.edges[k];
// Check program:
if (!this.edgePrograms[k])
this.edgePrograms[k] = renderer.initProgram(edgesGl);
// Render
if (this.edgeFloatArrays[k]) {
edgesGl.useProgram(this.edgePrograms[k]);
renderer.render(
edgesGl,
this.edgePrograms[k],
this.edgeFloatArrays[k].array,
{
settings: this.settings,
matrix: matrix,
width: this.width,
height: this.height,
ratio: this.camera.ratio,
scalingRatio: this.settings(options, 'webglOversamplingRatio'),
indicesData: this.edgeIndicesArrays[k]
}
);
}
}
}
}
if (drawNodes) {
// Enable blending:
nodesGl.blendFunc(nodesGl.SRC_ALPHA, nodesGl.ONE_MINUS_SRC_ALPHA);
nodesGl.enable(nodesGl.BLEND);
for (k in this.nodeFloatArrays) {
renderer = sigma.webgl.nodes[k];
// Check program:
if (!this.nodePrograms[k])
this.nodePrograms[k] = renderer.initProgram(nodesGl);
// Render
if (this.nodeFloatArrays[k]) {
nodesGl.useProgram(this.nodePrograms[k]);
renderer.render(
nodesGl,
this.nodePrograms[k],
this.nodeFloatArrays[k].array,
{
settings: this.settings,
matrix: matrix,
width: this.width,
height: this.height,
ratio: this.camera.ratio,
scalingRatio: this.settings(options, 'webglOversamplingRatio')
}
);
}
}
}
if (drawLabels) {
a = this.camera.quadtree.area(
this.camera.getRectangle(this.width, this.height)
);
// Apply camera view to these nodes:
this.camera.applyView(
undefined,
undefined,
{
nodes: a,
edges: [],
width: this.width,
height: this.height
}
);
o = function(key) {
return self.settings({
prefix: self.camera.prefix
}, key);
};
for (i = 0, l = a.length; i < l; i++)
if (!a[i].hidden)
(
sigma.canvas.labels[
a[i].type ||
this.settings(options, 'defaultNodeType')
] || sigma.canvas.labels.def
)(a[i], this.contexts.labels, o);
}
this.dispatchEvent('render');
return this;
};
/**
* This method creates a DOM element of the specified type, switches its
* position to "absolute", references it to the domElements attribute, and
* finally appends it to the container.
*
* @param {string} tag The label tag.
* @param {string} id The id of the element (to store it in
* "domElements").
* @param {?boolean} webgl Will init the WebGL context if true.
*/
sigma.renderers.webgl.prototype.initDOM = function(tag, id, webgl) {
var gl,
dom = document.createElement(tag),
self = this;
dom.style.position = 'absolute';
dom.setAttribute('class', 'sigma-' + id);
this.domElements[id] = dom;
this.container.appendChild(dom);
if (tag.toLowerCase() === 'canvas') {
this.contexts[id] = dom.getContext(webgl ? 'experimental-webgl' : '2d', {
preserveDrawingBuffer: true
});
// Adding webgl context loss listeners
if (webgl) {
dom.addEventListener('webglcontextlost', function(e) {
e.preventDefault();
}, false);
dom.addEventListener('webglcontextrestored', function(e) {
self.render();
}, false);
}
}
};
/**
* This method resizes each DOM elements in the container and stores the new
* dimensions. Then, it renders the graph.
*
* @param {?number} width The new width of the container.
* @param {?number} height The new height of the container.
* @return {sigma.renderers.webgl} Returns the instance itself.
*/
sigma.renderers.webgl.prototype.resize = function(w, h) {
var k,
oldWidth = this.width,
oldHeight = this.height,
pixelRatio = sigma.utils.getPixelRatio();
if (w !== undefined && h !== undefined) {
this.width = w;
this.height = h;
} else {
this.width = this.container.offsetWidth;
this.height = this.container.offsetHeight;
w = this.width;
h = this.height;
}
if (oldWidth !== this.width || oldHeight !== this.height) {
for (k in this.domElements) {
this.domElements[k].style.width = w + 'px';
this.domElements[k].style.height = h + 'px';
if (this.domElements[k].tagName.toLowerCase() === 'canvas') {
// If simple 2D canvas:
if (this.contexts[k] && this.contexts[k].scale) {
this.domElements[k].setAttribute('width', (w * pixelRatio) + 'px');
this.domElements[k].setAttribute('height', (h * pixelRatio) + 'px');
if (pixelRatio !== 1)
this.contexts[k].scale(pixelRatio, pixelRatio);
} else {
this.domElements[k].setAttribute(
'width',
(w * this.settings('webglOversamplingRatio')) + 'px'
);
this.domElements[k].setAttribute(
'height',
(h * this.settings('webglOversamplingRatio')) + 'px'
);
}
}
}
}
// Scale:
for (k in this.contexts)
if (this.contexts[k] && this.contexts[k].viewport)
this.contexts[k].viewport(
0,
0,
this.width * this.settings('webglOversamplingRatio'),
this.height * this.settings('webglOversamplingRatio')
);
return this;
};
/**
* This method clears each canvas.
*
* @return {sigma.renderers.webgl} Returns the instance itself.
*/
sigma.renderers.webgl.prototype.clear = function() {
this.contexts.labels.clearRect(0, 0, this.width, this.height);
this.contexts.nodes.clear(this.contexts.nodes.COLOR_BUFFER_BIT);
this.contexts.edges.clear(this.contexts.edges.COLOR_BUFFER_BIT);
return this;
};
/**
* This method kills contexts and other attributes.
*/
sigma.renderers.webgl.prototype.kill = function() {
var k,
captor;
// Kill captors:
while ((captor = this.captors.pop()))
captor.kill();
delete this.captors;
// Kill contexts:
for (k in this.domElements) {
this.domElements[k].parentNode.removeChild(this.domElements[k]);
delete this.domElements[k];
delete this.contexts[k];
}
delete this.domElements;
delete this.contexts;
};
/**
* The object "sigma.webgl.nodes" contains the different WebGL node
* renderers. The default one draw nodes as discs. Here are the attributes
* any node renderer must have:
*
* {number} POINTS The number of points required to draw a node.
* {number} ATTRIBUTES The number of attributes needed to draw one point.
* {function} addNode A function that adds a node to the data stack that
* will be given to the buffer. Here is the arguments:
* > {object} node
* > {number} index The node index in the
* nodes array.
* > {Float32Array} data The stack.
* > {object} options Some options.
* {function} render The function that will effectively render the nodes
* into the buffer.
* > {WebGLRenderingContext} gl
* > {WebGLProgram} program
* > {Float32Array} data The stack to give to the
* buffer.
* > {object} params An object containing some
* options, like width,
* height, the camera ratio.
* {function} initProgram The function that will initiate the program, with
* the relevant shaders and parameters. It must return
* the newly created program.
*
* Check sigma.webgl.nodes.def or sigma.webgl.nodes.fast to see how it
* works more precisely.
*/
sigma.utils.pkg('sigma.webgl.nodes');
/**
* The object "sigma.webgl.edges" contains the different WebGL edge
* renderers. The default one draw edges as direct lines. Here are the
* attributes any edge renderer must have:
*
* {number} POINTS The number of points required to draw an edge.
* {number} ATTRIBUTES The number of attributes needed to draw one point.
* {function} addEdge A function that adds an edge to the data stack that
* will be given to the buffer. Here is the arguments:
* > {object} edge
* > {object} source
* > {object} target
* > {Float32Array} data The stack.
* > {object} options Some options.
* {function} render The function that will effectively render the edges
* into the buffer.
* > {WebGLRenderingContext} gl
* > {WebGLProgram} program
* > {Float32Array} data The stack to give to the
* buffer.
* > {object} params An object containing some
* options, like width,
* height, the camera ratio.
* {function} initProgram The function that will initiate the program, with
* the relevant shaders and parameters. It must return
* the newly created program.
*
* Check sigma.webgl.edges.def or sigma.webgl.edges.fast to see how it
* works more precisely.
*/
sigma.utils.pkg('sigma.webgl.edges');
/**
* The object "sigma.canvas.labels" contains the different
* label renderers for the WebGL renderer. Since displaying texts in WebGL is
* definitely painful and since there a way less labels to display than nodes
* or edges, the default renderer simply renders them in a canvas.
*
* A labels renderer is a simple function, taking as arguments the related
* node, the renderer and a settings function.
*/
sigma.utils.pkg('sigma.canvas.labels');
}).call(this);
}.call(window));
/***/ }),
/***/ 71:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
sigma.utils.pkg('sigma.webgl.edges');
/**
* This edge renderer will display edges as arrows going from the source node
* to the target node. To deal with edge thicknesses, the lines are made of
* three triangles: two forming rectangles, with the gl.TRIANGLES drawing
* mode.
*
* It is expensive, since drawing a single edge requires 9 points, each
* having a lot of attributes.
*/
sigma.webgl.edges.arrow = {
POINTS: 9,
ATTRIBUTES: 11,
addEdge: function(edge, source, target, data, i, prefix, settings) {
var w = (edge[prefix + 'size'] || 1) / 2,
x1 = source[prefix + 'x'],
y1 = source[prefix + 'y'],
x2 = target[prefix + 'x'],
y2 = target[prefix + 'y'],
targetSize = target[prefix + 'size'],
color = edge.color;
if (!color)
switch (settings('edgeColor')) {
case 'source':
color = source.color || settings('defaultNodeColor');
break;
case 'target':
color = target.color || settings('defaultNodeColor');
break;
default:
color = settings('defaultEdgeColor');
break;
}
// Normalize color:
color = sigma.utils.floatColor(color);
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 0.0;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = 0.0;
data[i++] = color;
// Arrow head:
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 1.0;
data[i++] = -1.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = targetSize;
data[i++] = 1.0;
data[i++] = 0.0;
data[i++] = 1.0;
data[i++] = 1.0;
data[i++] = color;
},
render: function(gl, program, data, params) {
var buffer;
// Define attributes:
var positionLocation1 =
gl.getAttribLocation(program, 'a_pos1'),
positionLocation2 =
gl.getAttribLocation(program, 'a_pos2'),
thicknessLocation =
gl.getAttribLocation(program, 'a_thickness'),
targetSizeLocation =
gl.getAttribLocation(program, 'a_tSize'),
delayLocation =
gl.getAttribLocation(program, 'a_delay'),
minusLocation =
gl.getAttribLocation(program, 'a_minus'),
headLocation =
gl.getAttribLocation(program, 'a_head'),
headPositionLocation =
gl.getAttribLocation(program, 'a_headPosition'),
colorLocation =
gl.getAttribLocation(program, 'a_color'),
resolutionLocation =
gl.getUniformLocation(program, 'u_resolution'),
matrixLocation =
gl.getUniformLocation(program, 'u_matrix'),
matrixHalfPiLocation =
gl.getUniformLocation(program, 'u_matrixHalfPi'),
matrixHalfPiMinusLocation =
gl.getUniformLocation(program, 'u_matrixHalfPiMinus'),
ratioLocation =
gl.getUniformLocation(program, 'u_ratio'),
nodeRatioLocation =
gl.getUniformLocation(program, 'u_nodeRatio'),
arrowHeadLocation =
gl.getUniformLocation(program, 'u_arrowHead'),
scaleLocation =
gl.getUniformLocation(program, 'u_scale');
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.uniform2f(resolutionLocation, params.width, params.height);
gl.uniform1f(
ratioLocation,
params.ratio / Math.pow(params.ratio, params.settings('edgesPowRatio'))
);
gl.uniform1f(
nodeRatioLocation,
Math.pow(params.ratio, params.settings('nodesPowRatio')) /
params.ratio
);
gl.uniform1f(arrowHeadLocation, 5.0);
gl.uniform1f(scaleLocation, params.scalingRatio);
gl.uniformMatrix3fv(matrixLocation, false, params.matrix);
gl.uniformMatrix2fv(
matrixHalfPiLocation,
false,
sigma.utils.matrices.rotation(Math.PI / 2, true)
);
gl.uniformMatrix2fv(
matrixHalfPiMinusLocation,
false,
sigma.utils.matrices.rotation(-Math.PI / 2, true)
);
gl.enableVertexAttribArray(positionLocation1);
gl.enableVertexAttribArray(positionLocation2);
gl.enableVertexAttribArray(thicknessLocation);
gl.enableVertexAttribArray(targetSizeLocation);
gl.enableVertexAttribArray(delayLocation);
gl.enableVertexAttribArray(minusLocation);
gl.enableVertexAttribArray(headLocation);
gl.enableVertexAttribArray(headPositionLocation);
gl.enableVertexAttribArray(colorLocation);
gl.vertexAttribPointer(positionLocation1,
2,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.vertexAttribPointer(positionLocation2,
2,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
8
);
gl.vertexAttribPointer(thicknessLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
16
);
gl.vertexAttribPointer(targetSizeLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
20
);
gl.vertexAttribPointer(delayLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
24
);
gl.vertexAttribPointer(minusLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
28
);
gl.vertexAttribPointer(headLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
32
);
gl.vertexAttribPointer(headPositionLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
36
);
gl.vertexAttribPointer(colorLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
40
);
gl.drawArrays(
gl.TRIANGLES,
params.start || 0,
params.count || (data.length / this.ATTRIBUTES)
);
},
initProgram: function(gl) {
var vertexShader,
fragmentShader,
program;
vertexShader = sigma.utils.loadShader(
gl,
[
'attribute vec2 a_pos1;',
'attribute vec2 a_pos2;',
'attribute float a_thickness;',
'attribute float a_tSize;',
'attribute float a_delay;',
'attribute float a_minus;',
'attribute float a_head;',
'attribute float a_headPosition;',
'attribute float a_color;',
'uniform vec2 u_resolution;',
'uniform float u_ratio;',
'uniform float u_nodeRatio;',
'uniform float u_arrowHead;',
'uniform float u_scale;',
'uniform mat3 u_matrix;',
'uniform mat2 u_matrixHalfPi;',
'uniform mat2 u_matrixHalfPiMinus;',
'varying vec4 color;',
'void main() {',
// Find the good point:
'vec2 pos = normalize(a_pos2 - a_pos1);',
'mat2 matrix = (1.0 - a_head) *',
'(',
'a_minus * u_matrixHalfPiMinus +',
'(1.0 - a_minus) * u_matrixHalfPi',
') + a_head * (',
'a_headPosition * u_matrixHalfPiMinus * 0.6 +',
'(a_headPosition * a_headPosition - 1.0) * mat2(1.0)',
');',
'pos = a_pos1 + (',
// Deal with body:
'(1.0 - a_head) * a_thickness * u_ratio * matrix * pos +',
// Deal with head:
'a_head * u_arrowHead * a_thickness * u_ratio * matrix * pos +',
// Deal with delay:
'a_delay * pos * (',
'a_tSize / u_nodeRatio +',
'u_arrowHead * a_thickness * u_ratio',
')',
');',
// Scale from [[-1 1] [-1 1]] to the container:
'gl_Position = vec4(',
'((u_matrix * vec3(pos, 1)).xy /',
'u_resolution * 2.0 - 1.0) * vec2(1, -1),',
'0,',
'1',
');',
// Extract the color:
'float c = a_color;',
'color.b = mod(c, 256.0); c = floor(c / 256.0);',
'color.g = mod(c, 256.0); c = floor(c / 256.0);',
'color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;',
'color.a = 1.0;',
'}'
].join('\n'),
gl.VERTEX_SHADER
);
fragmentShader = sigma.utils.loadShader(
gl,
[
'precision mediump float;',
'varying vec4 color;',
'void main(void) {',
'gl_FragColor = color;',
'}'
].join('\n'),
gl.FRAGMENT_SHADER
);
program = sigma.utils.loadProgram(gl, [vertexShader, fragmentShader]);
return program;
}
};
})();
}.call(window));
/***/ }),
/***/ 72:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
sigma.utils.pkg('sigma.webgl.edges');
/**
* This edge renderer will display edges as lines going from the source node
* to the target node. To deal with edge thicknesses, the lines are made of
* two triangles forming rectangles, with the gl.TRIANGLES drawing mode.
*
* It is expensive, since drawing a single edge requires 6 points, each
* having 7 attributes (source position, target position, thickness, color
* and a flag indicating which vertice of the rectangle it is).
*/
sigma.webgl.edges.def = {
POINTS: 6,
ATTRIBUTES: 7,
addEdge: function(edge, source, target, data, i, prefix, settings) {
var w = (edge[prefix + 'size'] || 1) / 2,
x1 = source[prefix + 'x'],
y1 = source[prefix + 'y'],
x2 = target[prefix + 'x'],
y2 = target[prefix + 'y'],
color = edge.color;
if (!color)
switch (settings('edgeColor')) {
case 'source':
color = source.color || settings('defaultNodeColor');
break;
case 'target':
color = target.color || settings('defaultNodeColor');
break;
default:
color = settings('defaultEdgeColor');
break;
}
// Normalize color:
color = sigma.utils.floatColor(color);
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = 1.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = x1;
data[i++] = y1;
data[i++] = w;
data[i++] = 0.0;
data[i++] = color;
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = 1.0;
data[i++] = color;
data[i++] = x1;
data[i++] = y1;
data[i++] = x2;
data[i++] = y2;
data[i++] = w;
data[i++] = 0.0;
data[i++] = color;
},
render: function(gl, program, data, params) {
var buffer;
// Define attributes:
var colorLocation =
gl.getAttribLocation(program, 'a_color'),
positionLocation1 =
gl.getAttribLocation(program, 'a_position1'),
positionLocation2 =
gl.getAttribLocation(program, 'a_position2'),
thicknessLocation =
gl.getAttribLocation(program, 'a_thickness'),
minusLocation =
gl.getAttribLocation(program, 'a_minus'),
resolutionLocation =
gl.getUniformLocation(program, 'u_resolution'),
matrixLocation =
gl.getUniformLocation(program, 'u_matrix'),
matrixHalfPiLocation =
gl.getUniformLocation(program, 'u_matrixHalfPi'),
matrixHalfPiMinusLocation =
gl.getUniformLocation(program, 'u_matrixHalfPiMinus'),
ratioLocation =
gl.getUniformLocation(program, 'u_ratio'),
scaleLocation =
gl.getUniformLocation(program, 'u_scale');
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.uniform2f(resolutionLocation, params.width, params.height);
gl.uniform1f(
ratioLocation,
params.ratio / Math.pow(params.ratio, params.settings('edgesPowRatio'))
);
gl.uniform1f(scaleLocation, params.scalingRatio);
gl.uniformMatrix3fv(matrixLocation, false, params.matrix);
gl.uniformMatrix2fv(
matrixHalfPiLocation,
false,
sigma.utils.matrices.rotation(Math.PI / 2, true)
);
gl.uniformMatrix2fv(
matrixHalfPiMinusLocation,
false,
sigma.utils.matrices.rotation(-Math.PI / 2, true)
);
gl.enableVertexAttribArray(colorLocation);
gl.enableVertexAttribArray(positionLocation1);
gl.enableVertexAttribArray(positionLocation2);
gl.enableVertexAttribArray(thicknessLocation);
gl.enableVertexAttribArray(minusLocation);
gl.vertexAttribPointer(positionLocation1,
2,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.vertexAttribPointer(positionLocation2,
2,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
8
);
gl.vertexAttribPointer(thicknessLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
16
);
gl.vertexAttribPointer(minusLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
20
);
gl.vertexAttribPointer(colorLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
24
);
gl.drawArrays(
gl.TRIANGLES,
params.start || 0,
params.count || (data.length / this.ATTRIBUTES)
);
},
initProgram: function(gl) {
var vertexShader,
fragmentShader,
program;
vertexShader = sigma.utils.loadShader(
gl,
[
'attribute vec2 a_position1;',
'attribute vec2 a_position2;',
'attribute float a_thickness;',
'attribute float a_minus;',
'attribute float a_color;',
'uniform vec2 u_resolution;',
'uniform float u_ratio;',
'uniform float u_scale;',
'uniform mat3 u_matrix;',
'uniform mat2 u_matrixHalfPi;',
'uniform mat2 u_matrixHalfPiMinus;',
'varying vec4 color;',
'void main() {',
// Find the good point:
'vec2 position = a_thickness * u_ratio *',
'normalize(a_position2 - a_position1);',
'mat2 matrix = a_minus * u_matrixHalfPiMinus +',
'(1.0 - a_minus) * u_matrixHalfPi;',
'position = matrix * position + a_position1;',
// Scale from [[-1 1] [-1 1]] to the container:
'gl_Position = vec4(',
'((u_matrix * vec3(position, 1)).xy /',
'u_resolution * 2.0 - 1.0) * vec2(1, -1),',
'0,',
'1',
');',
// Extract the color:
'float c = a_color;',
'color.b = mod(c, 256.0); c = floor(c / 256.0);',
'color.g = mod(c, 256.0); c = floor(c / 256.0);',
'color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;',
'color.a = 1.0;',
'}'
].join('\n'),
gl.VERTEX_SHADER
);
fragmentShader = sigma.utils.loadShader(
gl,
[
'precision mediump float;',
'varying vec4 color;',
'void main(void) {',
'gl_FragColor = color;',
'}'
].join('\n'),
gl.FRAGMENT_SHADER
);
program = sigma.utils.loadProgram(gl, [vertexShader, fragmentShader]);
return program;
}
};
})();
}.call(window));
/***/ }),
/***/ 73:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
sigma.utils.pkg('sigma.webgl.edges');
/**
* This edge renderer will display edges as lines with the gl.LINES display
* mode. Since this mode does not support well thickness, edges are all drawn
* with the same thickness (3px), independantly of the edge attributes or the
* zooming ratio.
*/
sigma.webgl.edges.fast = {
POINTS: 2,
ATTRIBUTES: 3,
addEdge: function(edge, source, target, data, i, prefix, settings) {
var w = (edge[prefix + 'size'] || 1) / 2,
x1 = source[prefix + 'x'],
y1 = source[prefix + 'y'],
x2 = target[prefix + 'x'],
y2 = target[prefix + 'y'],
color = edge.color;
if (!color)
switch (settings('edgeColor')) {
case 'source':
color = source.color || settings('defaultNodeColor');
break;
case 'target':
color = target.color || settings('defaultNodeColor');
break;
default:
color = settings('defaultEdgeColor');
break;
}
// Normalize color:
color = sigma.utils.floatColor(color);
data[i++] = x1;
data[i++] = y1;
data[i++] = color;
data[i++] = x2;
data[i++] = y2;
data[i++] = color;
},
render: function(gl, program, data, params) {
var buffer;
// Define attributes:
var colorLocation =
gl.getAttribLocation(program, 'a_color'),
positionLocation =
gl.getAttribLocation(program, 'a_position'),
resolutionLocation =
gl.getUniformLocation(program, 'u_resolution'),
matrixLocation =
gl.getUniformLocation(program, 'u_matrix');
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
gl.uniform2f(resolutionLocation, params.width, params.height);
gl.uniformMatrix3fv(matrixLocation, false, params.matrix);
gl.enableVertexAttribArray(positionLocation);
gl.enableVertexAttribArray(colorLocation);
gl.vertexAttribPointer(positionLocation,
2,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.vertexAttribPointer(colorLocation,
1,
gl.FLOAT,
false,
this.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT,
8
);
gl.lineWidth(3);
gl.drawArrays(
gl.LINES,
params.start || 0,
params.count || (data.length / this.ATTRIBUTES)
);
},
initProgram: function(gl) {
var vertexShader,
fragmentShader,
program;
vertexShader = sigma.utils.loadShader(
gl,
[
'attribute vec2 a_position;',
'attribute float a_color;',
'uniform vec2 u_resolution;',
'uniform mat3 u_matrix;',
'varying vec4 color;',
'void main() {',
// Scale from [[-1 1] [-1 1]] to the container:
'gl_Position = vec4(',
'((u_matrix * vec3(a_position, 1)).xy /',
'u_resolution * 2.0 - 1.0) * vec2(1, -1),',
'0,',
'1',
');',
// Extract the color:
'float c = a_color;',
'color.b = mod(c, 256.0); c = floor(c / 256.0);',
'color.g = mod(c, 256.0); c = floor(c / 256.0);',
'color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;',
'color.a = 1.0;',
'}'
].join('\n'),
gl.VERTEX_SHADER
);
fragmentShader = sigma.utils.loadShader(
gl,
[
'precision mediump float;',
'varying vec4 color;',
'void main(void) {',
'gl_FragColor = color;',
'}'
].join('\n'),
gl.FRAGMENT_SHADER
);
program = sigma.utils.loadProgram(gl, [vertexShader, fragmentShader]);
return program;
}
};
})();
}.call(window));
/***/ }),
/***/ 74:
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
sigma.utils.pkg('sigma.webgl.nodes');
/**
* This node renderer will display nodes as discs, shaped in triangles with
* the gl.TRIANGLES display mode. So, to be more precise, to draw one node,
* it will store three times the center of node, with the color and the size,
* and an angle indicating which "corner" of the triangle to draw.
*
* The fragment shader does not deal with anti-aliasing, so make sure that
* you deal with it somewhere else in the code (by default, the WebGL
* renderer will oversample the rendering through the webglOversamplingRatio
* value).
*/
sigma.webgl.nodes.def = {
POINTS: 3,
ATTRIBUTES: 5,
addNode: function(node, data, i, prefix, settings) {
var color = sigma.utils.floatColor(
node.color || settings('defaultNodeColor')
);
data[i++] = node[prefix + 'x'];
data[i++] = node[prefix + 'y'];
data[i++] = node[prefix + 'size'];
data[i++] = color;
data[i++] = 0;
data[i++] = node[prefix + 'x'];
data[i++] = node[prefix + 'y'];