react-sigma-conglei
Version:
Lightweight but powerful library for drawing network graphs built on top of dunnock/react-sigma
1,733 lines (1,492 loc) • 274 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 = 81);
/******/ })
/************************************************************************/
/******/ (Array(34).concat([
/* 34 */
/***/ (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.captors');
/**
* The user inputs default captor. It deals with mouse events, keyboards
* events and touch events.
*
* @param {DOMElement} target The DOM element where the listeners will be
* bound.
* @param {camera} camera The camera related to the target.
* @param {configurable} settings The settings function.
* @return {sigma.captor} The fresh new captor instance.
*/
sigma.captors.mouse = function(target, camera, settings) {
var _self = this,
_target = target,
_camera = camera,
_settings = settings,
// CAMERA MANAGEMENT:
// ******************
// The camera position when the user starts dragging:
_startCameraX,
_startCameraY,
_startCameraAngle,
// The latest stage position:
_lastCameraX,
_lastCameraY,
_lastCameraAngle,
_lastCameraRatio,
// MOUSE MANAGEMENT:
// *****************
// The mouse position when the user starts dragging:
_startMouseX,
_startMouseY,
_isMouseDown,
_isMoving,
_hasDragged,
_downStartTime,
_movingTimeoutId;
sigma.classes.dispatcher.extend(this);
sigma.utils.doubleClick(_target, 'click', _doubleClickHandler);
_target.addEventListener('DOMMouseScroll', _wheelHandler, false);
_target.addEventListener('mousewheel', _wheelHandler, false);
_target.addEventListener('mousemove', _moveHandler, false);
_target.addEventListener('mousedown', _downHandler, false);
_target.addEventListener('click', _clickHandler, false);
_target.addEventListener('mouseout', _outHandler, false);
document.addEventListener('mouseup', _upHandler, false);
/**
* This method unbinds every handlers that makes the captor work.
*/
this.kill = function() {
sigma.utils.unbindDoubleClick(_target, 'click');
_target.removeEventListener('DOMMouseScroll', _wheelHandler);
_target.removeEventListener('mousewheel', _wheelHandler);
_target.removeEventListener('mousemove', _moveHandler);
_target.removeEventListener('mousedown', _downHandler);
_target.removeEventListener('click', _clickHandler);
_target.removeEventListener('mouseout', _outHandler);
document.removeEventListener('mouseup', _upHandler);
};
// MOUSE EVENTS:
// *************
/**
* The handler listening to the 'move' mouse event. It will effectively
* drag the graph.
*
* @param {event} e A mouse event.
*/
function _moveHandler(e) {
var x,
y,
pos;
// Dispatch event:
if (_settings('mouseEnabled')) {
_self.dispatchEvent('mousemove',
sigma.utils.mouseCoords(e));
if (_isMouseDown) {
_isMoving = true;
_hasDragged = true;
if (_movingTimeoutId)
clearTimeout(_movingTimeoutId);
_movingTimeoutId = setTimeout(function() {
_isMoving = false;
}, _settings('dragTimeout'));
sigma.misc.animation.killAll(_camera);
_camera.isMoving = true;
pos = _camera.cameraPosition(
sigma.utils.getX(e) - _startMouseX,
sigma.utils.getY(e) - _startMouseY,
true
);
x = _startCameraX - pos.x;
y = _startCameraY - pos.y;
if (x !== _camera.x || y !== _camera.y) {
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
_camera.goTo({
x: x,
y: y
});
}
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
e.stopPropagation();
return false;
}
}
}
/**
* The handler listening to the 'up' mouse event. It will stop dragging the
* graph.
*
* @param {event} e A mouse event.
*/
function _upHandler(e) {
if (_settings('mouseEnabled') && _isMouseDown) {
_isMouseDown = false;
if (_movingTimeoutId)
clearTimeout(_movingTimeoutId);
_camera.isMoving = false;
var x = sigma.utils.getX(e),
y = sigma.utils.getY(e);
if (_isMoving) {
sigma.misc.animation.killAll(_camera);
sigma.misc.animation.camera(
_camera,
{
x: _camera.x +
_settings('mouseInertiaRatio') * (_camera.x - _lastCameraX),
y: _camera.y +
_settings('mouseInertiaRatio') * (_camera.y - _lastCameraY)
},
{
easing: 'quadraticOut',
duration: _settings('mouseInertiaDuration')
}
);
} else if (
_startMouseX !== x ||
_startMouseY !== y
)
_camera.goTo({
x: _camera.x,
y: _camera.y
});
_self.dispatchEvent('mouseup',
sigma.utils.mouseCoords(e));
// Update _isMoving flag:
_isMoving = false;
}
}
/**
* The handler listening to the 'down' mouse event. It will start observing
* the mouse position for dragging the graph.
*
* @param {event} e A mouse event.
*/
function _downHandler(e) {
if (_settings('mouseEnabled')) {
_startCameraX = _camera.x;
_startCameraY = _camera.y;
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
_startMouseX = sigma.utils.getX(e);
_startMouseY = sigma.utils.getY(e);
_hasDragged = false;
_downStartTime = (new Date()).getTime();
switch (e.which) {
case 2:
// Middle mouse button pressed
// Do nothing.
break;
case 3:
// Right mouse button pressed
_self.dispatchEvent('rightclick',
sigma.utils.mouseCoords(e, _startMouseX, _startMouseY));
break;
// case 1:
default:
// Left mouse button pressed
_isMouseDown = true;
_self.dispatchEvent('mousedown',
sigma.utils.mouseCoords(e, _startMouseX, _startMouseY));
}
}
}
/**
* The handler listening to the 'out' mouse event. It will just redispatch
* the event.
*
* @param {event} e A mouse event.
*/
function _outHandler(e) {
if (_settings('mouseEnabled'))
_self.dispatchEvent('mouseout');
}
/**
* The handler listening to the 'click' mouse event. It will redispatch the
* click event, but with normalized X and Y coordinates.
*
* @param {event} e A mouse event.
*/
function _clickHandler(e) {
if (_settings('mouseEnabled')) {
var event = sigma.utils.mouseCoords(e);
event.isDragging =
(((new Date()).getTime() - _downStartTime) > 100) && _hasDragged;
_self.dispatchEvent('click', event);
}
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
e.stopPropagation();
return false;
}
/**
* The handler listening to the double click custom event. It will
* basically zoom into the graph.
*
* @param {event} e A mouse event.
*/
function _doubleClickHandler(e) {
var pos,
ratio,
animation;
if (_settings('mouseEnabled')) {
ratio = 1 / _settings('doubleClickZoomingRatio');
_self.dispatchEvent('doubleclick',
sigma.utils.mouseCoords(e, _startMouseX, _startMouseY));
if (_settings('doubleClickEnabled')) {
pos = _camera.cameraPosition(
sigma.utils.getX(e) - sigma.utils.getCenter(e).x,
sigma.utils.getY(e) - sigma.utils.getCenter(e).y,
true
);
animation = {
duration: _settings('doubleClickZoomDuration')
};
sigma.utils.zoomTo(_camera, pos.x, pos.y, ratio, animation);
}
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
e.stopPropagation();
return false;
}
}
/**
* The handler listening to the 'wheel' mouse event. It will basically zoom
* in or not into the graph.
*
* @param {event} e A mouse event.
*/
function _wheelHandler(e) {
var pos,
ratio,
animation,
wheelDelta = sigma.utils.getDelta(e);
if (_settings('mouseEnabled') && _settings('mouseWheelEnabled') && wheelDelta !== 0) {
ratio = wheelDelta > 0 ?
1 / _settings('zoomingRatio') :
_settings('zoomingRatio');
pos = _camera.cameraPosition(
sigma.utils.getX(e) - sigma.utils.getCenter(e).x,
sigma.utils.getY(e) - sigma.utils.getCenter(e).y,
true
);
animation = {
duration: _settings('mouseZoomDuration')
};
sigma.utils.zoomTo(_camera, pos.x, pos.y, ratio, animation);
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
e.stopPropagation();
return false;
}
}
};
}).call(this);
}.call(window));
/***/ }),
/* 35 */
/***/ (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.captors');
/**
* The user inputs default captor. It deals with mouse events, keyboards
* events and touch events.
*
* @param {DOMElement} target The DOM element where the listeners will be
* bound.
* @param {camera} camera The camera related to the target.
* @param {configurable} settings The settings function.
* @return {sigma.captor} The fresh new captor instance.
*/
sigma.captors.touch = function(target, camera, settings) {
var _self = this,
_target = target,
_camera = camera,
_settings = settings,
// CAMERA MANAGEMENT:
// ******************
// The camera position when the user starts dragging:
_startCameraX,
_startCameraY,
_startCameraAngle,
_startCameraRatio,
// The latest stage position:
_lastCameraX,
_lastCameraY,
_lastCameraAngle,
_lastCameraRatio,
// TOUCH MANAGEMENT:
// *****************
// Touches that are down:
_downTouches = [],
_startTouchX0,
_startTouchY0,
_startTouchX1,
_startTouchY1,
_startTouchAngle,
_startTouchDistance,
_touchMode,
_isMoving,
_doubleTap,
_movingTimeoutId;
sigma.classes.dispatcher.extend(this);
sigma.utils.doubleClick(_target, 'touchstart', _doubleTapHandler);
_target.addEventListener('touchstart', _handleStart, false);
_target.addEventListener('touchend', _handleLeave, false);
_target.addEventListener('touchcancel', _handleLeave, false);
_target.addEventListener('touchleave', _handleLeave, false);
_target.addEventListener('touchmove', _handleMove, false);
function position(e) {
var offset = sigma.utils.getOffset(_target);
return {
x: e.pageX - offset.left,
y: e.pageY - offset.top
};
}
/**
* This method unbinds every handlers that makes the captor work.
*/
this.kill = function() {
sigma.utils.unbindDoubleClick(_target, 'touchstart');
_target.addEventListener('touchstart', _handleStart);
_target.addEventListener('touchend', _handleLeave);
_target.addEventListener('touchcancel', _handleLeave);
_target.addEventListener('touchleave', _handleLeave);
_target.addEventListener('touchmove', _handleMove);
};
// TOUCH EVENTS:
// *************
/**
* The handler listening to the 'touchstart' event. It will set the touch
* mode ("_touchMode") and start observing the user touch moves.
*
* @param {event} e A touch event.
*/
function _handleStart(e) {
if (_settings('touchEnabled')) {
var x0,
x1,
y0,
y1,
pos0,
pos1;
_downTouches = e.touches;
switch (_downTouches.length) {
case 1:
_camera.isMoving = true;
_touchMode = 1;
_startCameraX = _camera.x;
_startCameraY = _camera.y;
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
pos0 = position(_downTouches[0]);
_startTouchX0 = pos0.x;
_startTouchY0 = pos0.y;
break;
case 2:
_camera.isMoving = true;
_touchMode = 2;
pos0 = position(_downTouches[0]);
pos1 = position(_downTouches[1]);
x0 = pos0.x;
y0 = pos0.y;
x1 = pos1.x;
y1 = pos1.y;
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
_startCameraAngle = _camera.angle;
_startCameraRatio = _camera.ratio;
_startCameraX = _camera.x;
_startCameraY = _camera.y;
_startTouchX0 = x0;
_startTouchY0 = y0;
_startTouchX1 = x1;
_startTouchY1 = y1;
_startTouchAngle = Math.atan2(
_startTouchY1 - _startTouchY0,
_startTouchX1 - _startTouchX0
);
_startTouchDistance = Math.sqrt(
(_startTouchY1 - _startTouchY0) *
(_startTouchY1 - _startTouchY0) +
(_startTouchX1 - _startTouchX0) *
(_startTouchX1 - _startTouchX0)
);
e.preventDefault();
return false;
}
}
}
/**
* The handler listening to the 'touchend', 'touchcancel' and 'touchleave'
* event. It will update the touch mode if there are still at least one
* finger, and stop dragging else.
*
* @param {event} e A touch event.
*/
function _handleLeave(e) {
if (_settings('touchEnabled')) {
_downTouches = e.touches;
var inertiaRatio = _settings('touchInertiaRatio');
if (_movingTimeoutId) {
_isMoving = false;
clearTimeout(_movingTimeoutId);
}
switch (_touchMode) {
case 2:
if (e.touches.length === 1) {
_handleStart(e);
e.preventDefault();
break;
}
/* falls through */
case 1:
_camera.isMoving = false;
_self.dispatchEvent('stopDrag');
if (_isMoving) {
_doubleTap = false;
sigma.misc.animation.camera(
_camera,
{
x: _camera.x +
inertiaRatio * (_camera.x - _lastCameraX),
y: _camera.y +
inertiaRatio * (_camera.y - _lastCameraY)
},
{
easing: 'quadraticOut',
duration: _settings('touchInertiaDuration')
}
);
}
_isMoving = false;
_touchMode = 0;
break;
}
}
}
/**
* The handler listening to the 'touchmove' event. It will effectively drag
* the graph, and eventually zooms and turn it if the user is using two
* fingers.
*
* @param {event} e A touch event.
*/
function _handleMove(e) {
if (!_doubleTap && _settings('touchEnabled')) {
var x0,
x1,
y0,
y1,
cos,
sin,
end,
pos0,
pos1,
diff,
start,
dAngle,
dRatio,
newStageX,
newStageY,
newStageRatio,
newStageAngle;
_downTouches = e.touches;
_isMoving = true;
if (_movingTimeoutId)
clearTimeout(_movingTimeoutId);
_movingTimeoutId = setTimeout(function() {
_isMoving = false;
}, _settings('dragTimeout'));
switch (_touchMode) {
case 1:
pos0 = position(_downTouches[0]);
x0 = pos0.x;
y0 = pos0.y;
diff = _camera.cameraPosition(
x0 - _startTouchX0,
y0 - _startTouchY0,
true
);
newStageX = _startCameraX - diff.x;
newStageY = _startCameraY - diff.y;
if (newStageX !== _camera.x || newStageY !== _camera.y) {
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
_camera.goTo({
x: newStageX,
y: newStageY
});
_self.dispatchEvent('mousemove',
sigma.utils.mouseCoords(e, pos0.x, pos0.y));
_self.dispatchEvent('drag');
}
break;
case 2:
pos0 = position(_downTouches[0]);
pos1 = position(_downTouches[1]);
x0 = pos0.x;
y0 = pos0.y;
x1 = pos1.x;
y1 = pos1.y;
start = _camera.cameraPosition(
(_startTouchX0 + _startTouchX1) / 2 -
sigma.utils.getCenter(e).x,
(_startTouchY0 + _startTouchY1) / 2 -
sigma.utils.getCenter(e).y,
true
);
end = _camera.cameraPosition(
(x0 + x1) / 2 - sigma.utils.getCenter(e).x,
(y0 + y1) / 2 - sigma.utils.getCenter(e).y,
true
);
dAngle = Math.atan2(y1 - y0, x1 - x0) - _startTouchAngle;
dRatio = Math.sqrt(
(y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0)
) / _startTouchDistance;
// Translation:
x0 = start.x;
y0 = start.y;
// Homothetic transformation:
newStageRatio = _startCameraRatio / dRatio;
x0 = x0 * dRatio;
y0 = y0 * dRatio;
// Rotation:
newStageAngle = _startCameraAngle - dAngle;
cos = Math.cos(-dAngle);
sin = Math.sin(-dAngle);
x1 = x0 * cos + y0 * sin;
y1 = y0 * cos - x0 * sin;
x0 = x1;
y0 = y1;
// Finalize:
newStageX = x0 - end.x + _startCameraX;
newStageY = y0 - end.y + _startCameraY;
if (
newStageRatio !== _camera.ratio ||
newStageAngle !== _camera.angle ||
newStageX !== _camera.x ||
newStageY !== _camera.y
) {
_lastCameraX = _camera.x;
_lastCameraY = _camera.y;
_lastCameraAngle = _camera.angle;
_lastCameraRatio = _camera.ratio;
_camera.goTo({
x: newStageX,
y: newStageY,
angle: newStageAngle,
ratio: newStageRatio
});
_self.dispatchEvent('drag');
}
break;
}
e.preventDefault();
return false;
}
}
/**
* The handler listening to the double tap custom event. It will
* basically zoom into the graph.
*
* @param {event} e A touch event.
*/
function _doubleTapHandler(e) {
var pos,
ratio,
animation;
if (e.touches && e.touches.length === 1 && _settings('touchEnabled')) {
_doubleTap = true;
ratio = 1 / _settings('doubleClickZoomingRatio');
pos = position(e.touches[0]);
_self.dispatchEvent('doubleclick',
sigma.utils.mouseCoords(e, pos.x, pos.y));
if (_settings('doubleClickEnabled')) {
pos = _camera.cameraPosition(
pos.x - sigma.utils.getCenter(e).x,
pos.y - sigma.utils.getCenter(e).y,
true
);
animation = {
duration: _settings('doubleClickZoomDuration'),
onComplete: function() {
_doubleTap = false;
}
};
sigma.utils.zoomTo(_camera, pos.x, pos.y, ratio, animation);
}
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
e.stopPropagation();
return false;
}
}
};
}).call(this);
}.call(window));
/***/ }),
/* 36 */
/***/ (function(module, exports) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function(undefined) {
'use strict';
if (typeof sigma === 'undefined')
throw 'sigma is not declared';
sigma.utils.pkg('sigma.classes');
/**
* The camera constructor. It just initializes its attributes and methods.
*
* @param {string} id The id.
* @param {sigma.classes.graph} graph The graph.
* @param {configurable} settings The settings function.
* @param {?object} options Eventually some overriding options.
* @return {camera} Returns the fresh new camera instance.
*/
sigma.classes.camera = function(id, graph, settings, options) {
sigma.classes.dispatcher.extend(this);
Object.defineProperty(this, 'graph', {
value: graph
});
Object.defineProperty(this, 'id', {
value: id
});
Object.defineProperty(this, 'readPrefix', {
value: 'read_cam' + id + ':'
});
Object.defineProperty(this, 'prefix', {
value: 'cam' + id + ':'
});
this.x = 0;
this.y = 0;
this.ratio = 1;
this.angle = 0;
this.isAnimated = false;
this.settings = (typeof options === 'object' && options) ?
settings.embedObject(options) :
settings;
};
/**
* Updates the camera position.
*
* @param {object} coordinates The new coordinates object.
* @return {camera} Returns the camera.
*/
sigma.classes.camera.prototype.goTo = function(coordinates) {
if (!this.settings('enableCamera'))
return this;
var i,
l,
c = coordinates || {},
keys = ['x', 'y', 'ratio', 'angle'];
for (i = 0, l = keys.length; i < l; i++)
if (c[keys[i]] !== undefined) {
if (typeof c[keys[i]] === 'number' && !isNaN(c[keys[i]]))
this[keys[i]] = c[keys[i]];
else
throw 'Value for "' + keys[i] + '" is not a number.';
}
this.dispatchEvent('coordinatesUpdated');
return this;
};
/**
* This method takes a graph and computes for each node and edges its
* coordinates relatively to the center of the camera. Basically, it will
* compute the coordinates that will be used by the graphic renderers.
*
* Since it should be possible to use different cameras and different
* renderers, it is possible to specify a prefix to put before the new
* coordinates (to get something like "node.camera1_x")
*
* @param {?string} read The prefix of the coordinates to read.
* @param {?string} write The prefix of the coordinates to write.
* @param {?object} options Eventually an object of options. Those can be:
* - A restricted nodes array.
* - A restricted edges array.
* - A width.
* - A height.
* @return {camera} Returns the camera.
*/
sigma.classes.camera.prototype.applyView = function(read, write, options) {
options = options || {};
write = write !== undefined ? write : this.prefix;
read = read !== undefined ? read : this.readPrefix;
var nodes = options.nodes || this.graph.nodes(),
edges = options.edges || this.graph.edges();
var i,
l,
node,
relCos = Math.cos(this.angle) / this.ratio,
relSin = Math.sin(this.angle) / this.ratio,
nodeRatio = Math.pow(this.ratio, this.settings('nodesPowRatio')),
edgeRatio = Math.pow(this.ratio, this.settings('edgesPowRatio')),
xOffset = (options.width || 0) / 2 - this.x * relCos - this.y * relSin,
yOffset = (options.height || 0) / 2 - this.y * relCos + this.x * relSin;
for (i = 0, l = nodes.length; i < l; i++) {
node = nodes[i];
node[write + 'x'] =
(node[read + 'x'] || 0) * relCos +
(node[read + 'y'] || 0) * relSin +
xOffset;
node[write + 'y'] =
(node[read + 'y'] || 0) * relCos -
(node[read + 'x'] || 0) * relSin +
yOffset;
node[write + 'size'] =
(node[read + 'size'] || 0) /
nodeRatio;
}
for (i = 0, l = edges.length; i < l; i++) {
edges[i][write + 'size'] =
(edges[i][read + 'size'] || 0) /
edgeRatio;
}
return this;
};
/**
* This function converts the coordinates of a point from the frame of the
* camera to the frame of the graph.
*
* @param {number} x The X coordinate of the point in the frame of the
* camera.
* @param {number} y The Y coordinate of the point in the frame of the
* camera.
* @return {object} The point coordinates in the frame of the graph.
*/
sigma.classes.camera.prototype.graphPosition = function(x, y, vector) {
var X = 0,
Y = 0,
cos = Math.cos(this.angle),
sin = Math.sin(this.angle);
// Revert the origin differential vector:
if (!vector) {
X = - (this.x * cos + this.y * sin) / this.ratio;
Y = - (this.y * cos - this.x * sin) / this.ratio;
}
return {
x: (x * cos + y * sin) / this.ratio + X,
y: (y * cos - x * sin) / this.ratio + Y
};
};
/**
* This function converts the coordinates of a point from the frame of the
* graph to the frame of the camera.
*
* @param {number} x The X coordinate of the point in the frame of the
* graph.
* @param {number} y The Y coordinate of the point in the frame of the
* graph.
* @return {object} The point coordinates in the frame of the camera.
*/
sigma.classes.camera.prototype.cameraPosition = function(x, y, vector) {
var X = 0,
Y = 0,
cos = Math.cos(this.angle),
sin = Math.sin(this.angle);
// Revert the origin differential vector:
if (!vector) {
X = - (this.x * cos + this.y * sin) / this.ratio;
Y = - (this.y * cos - this.x * sin) / this.ratio;
}
return {
x: ((x - X) * cos - (y - Y) * sin) * this.ratio,
y: ((y - Y) * cos + (x - X) * sin) * this.ratio
};
};
/**
* This method returns the transformation matrix of the camera. This is
* especially useful to apply the camera view directly in shaders, in case of
* WebGL rendering.
*
* @return {array} The transformation matrix.
*/
sigma.classes.camera.prototype.getMatrix = function() {
var scale = sigma.utils.matrices.scale(1 / this.ratio),
rotation = sigma.utils.matrices.rotation(this.angle),
translation = sigma.utils.matrices.translation(-this.x, -this.y),
matrix = sigma.utils.matrices.multiply(
translation,
sigma.utils.matrices.multiply(
rotation,
scale
)
);
return matrix;
};
/**
* Taking a width and a height as parameters, this method returns the
* coordinates of the rectangle representing the camera on screen, in the
* graph's referentiel.
*
* To keep displaying labels of nodes going out of the screen, the method
* keeps a margin around the screen in the returned rectangle.
*
* @param {number} width The width of the screen.
* @param {number} height The height of the screen.
* @return {object} The rectangle as x1, y1, x2 and y2, representing
* two opposite points.
*/
sigma.classes.camera.prototype.getRectangle = function(width, height) {
var widthVect = this.cameraPosition(width, 0, true),
heightVect = this.cameraPosition(0, height, true),
centerVect = this.cameraPosition(width / 2, height / 2, true),
marginX = this.cameraPosition(width / 4, 0, true).x,
marginY = this.cameraPosition(0, height / 4, true).y;
return {
x1: this.x - centerVect.x - marginX,
y1: this.y - centerVect.y - marginY,
x2: this.x - centerVect.x + marginX + widthVect.x,
y2: this.y - centerVect.y - marginY + widthVect.y,
height: Math.sqrt(
Math.pow(heightVect.x, 2) +
Math.pow(heightVect.y + 2 * marginY, 2)
)
};
};
}).call(this);
}.call(window));
/***/ }),
/* 37 */
/***/ (function(module, exports, __webpack_require__) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
/**
* This utils aims to facilitate the manipulation of each instance setting.
* Using a function instead of an object brings two main advantages: First,
* it will be easier in the future to catch settings updates through a
* function than an object. Second, giving it a full object will "merge" it
* to the settings object properly, keeping us to have to always add a loop.
*
* @return {configurable} The "settings" function.
*/
var configurable = function() {
var i,
l,
data = {},
datas = Array.prototype.slice.call(arguments, 0);
/**
* The method to use to set or get any property of this instance.
*
* @param {string|object} a1 If it is a string and if a2 is undefined,
* then it will return the corresponding
* property. If it is a string and if a2 is
* set, then it will set a2 as the property
* corresponding to a1, and return this. If
* it is an object, then each pair string +
* object(or any other type) will be set as a
* property.
* @param {*?} a2 The new property corresponding to a1 if a1
* is a string.
* @return {*|configurable} Returns itself or the corresponding
* property.
*
* Polymorphism:
* *************
* Here are some basic use examples:
*
* > settings = new configurable();
* > settings('mySetting', 42);
* > settings('mySetting'); // Logs: 42
* > settings('mySetting', 123);
* > settings('mySetting'); // Logs: 123
* > settings({mySetting: 456});
* > settings('mySetting'); // Logs: 456
*
* Also, it is possible to use the function as a fallback:
* > settings({mySetting: 'abc'}, 'mySetting'); // Logs: 'abc'
* > settings({hisSetting: 'abc'}, 'mySetting'); // Logs: 456
*/
var settings = function(a1, a2) {
var o,
i,
l,
k;
if (arguments.length === 1 && typeof a1 === 'string') {
if (data[a1] !== undefined)
return data[a1];
for (i = 0, l = datas.length; i < l; i++)
if (datas[i][a1] !== undefined)
return datas[i][a1];
return undefined;
} else if (typeof a1 === 'object' && typeof a2 === 'string') {
return (a1 || {})[a2] !== undefined ? a1[a2] : settings(a2);
} else {
o = (typeof a1 === 'object' && a2 === undefined) ? a1 : {};
if (typeof a1 === 'string')
o[a1] = a2;
for (i = 0, k = Object.keys(o), l = k.length; i < l; i++)
data[k[i]] = o[k[i]];
return this;
}
};
/**
* This method returns a new configurable function, with new objects
*
* @param {object*} Any number of objects to search in.
* @return {function} Returns the function. Check its documentation to know
* more about how it works.
*/
settings.embedObjects = function() {
var args = datas.concat(
data
).concat(
Array.prototype.splice.call(arguments, 0)
);
return configurable.apply({}, args);
};
// Initialize
for (i = 0, l = arguments.length; i < l; i++)
settings(arguments[i]);
return settings;
};
/**
* EXPORT:
* *******
*/
if (typeof this.sigma !== 'undefined') {
this.sigma.classes = this.sigma.classes || {};
this.sigma.classes.configurable = configurable;
} else if (true) {
if (typeof module !== 'undefined' && module.exports)
exports = module.exports = configurable;
exports.configurable = configurable;
} else
this.configurable = configurable;
}).call(this);
}.call(window));
/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function() {
'use strict';
/**
* Dispatcher constructor.
*
* @return {dispatcher} The new dispatcher instance.
*/
var dispatcher = function() {
Object.defineProperty(this, '_handlers', {
value: {}
});
};
/**
* Will execute the handler everytime that the indicated event (or the
* indicated events) will be triggered.
*
* @param {string} events The name of the event (or the events
* separated by spaces).
* @param {function(Object)} handler The handler to bind.
* @return {dispatcher} Returns the instance itself.
*/
dispatcher.prototype.bind = function(events, handler) {
var i,
l,
event,
eArray;
if (
arguments.length === 1 &&
typeof arguments[0] === 'object'
)
for (events in arguments[0])
this.bind(events, arguments[0][events]);
else if (
arguments.length === 2 &&
typeof arguments[1] === 'function'
) {
eArray = typeof events === 'string' ? events.split(' ') : events;
for (i = 0, l = eArray.length; i !== l; i += 1) {
event = eArray[i];
// Check that event is not '':
if (!event)
continue;
if (!this._handlers[event])
this._handlers[event] = [];
// Using an object instead of directly the handler will make possible
// later to add flags
this._handlers[event].push({
handler: handler
});
}
} else
throw 'bind: Wrong arguments.';
return this;
};
/**
* Removes the handler from a specified event (or specified events).
*
* @param {?string} events The name of the event (or the events
* separated by spaces). If undefined,
* then all handlers are removed.
* @param {?function(object)} handler The handler to unbind. If undefined,
* each handler bound to the event or the
* events will be removed.
* @return {dispatcher} Returns the instance itself.
*/
dispatcher.prototype.unbind = function(events, handler) {
var i,
n,
j,
m,
k,
a,
event,
eArray = typeof events === 'string' ? events.split(' ') : events;
if (!arguments.length) {
for (k in this._handlers)
delete this._handlers[k];
return this;
}
if (handler) {
for (i = 0, n = eArray.length; i !== n; i += 1) {
event = eArray[i];
if (this._handlers[event]) {
a = [];
for (j = 0, m = this._handlers[event].length; j !== m; j += 1)
if (this._handlers[event][j].handler !== handler)
a.push(this._handlers[event][j]);
this._handlers[event] = a;
}
if (this._handlers[event] && this._handlers[event].length === 0)
delete this._handlers[event];
}
} else
for (i = 0, n = eArray.length; i !== n; i += 1)
delete this._handlers[eArray[i]];
return this;
};
/**
* Executes each handler bound to the event
*
* @param {string} events The name of the event (or the events separated
* by spaces).
* @param {?object} data The content of the event (optional).
* @return {dispatcher} Returns the instance itself.
*/
dispatcher.prototype.dispatchEvent = function(events, data) {
var i,
n,
j,
m,
a,
event,
eventName,
self = this,
eArray = typeof events === 'string' ? events.split(' ') : events;
data = data === undefined ? {} : data;
for (i = 0, n = eArray.length; i !== n; i += 1) {
eventName = eArray[i];
if (this._handlers[eventName]) {
event = self.getEvent(eventName, data);
a = [];
for (j = 0, m = this._handlers[eventName].length; j !== m; j += 1) {
this._handlers[eventName][j].handler(event);
if (!this._handlers[eventName][j].one)
a.push(this._handlers[eventName][j]);
}
this._handlers[eventName] = a;
}
}
return this;
};
/**
* Return an event object.
*
* @param {string} events The name of the event.
* @param {?object} data The content of the event (optional).
* @return {object} Returns the instance itself.
*/
dispatcher.prototype.getEvent = function(event, data) {
return {
type: event,
data: data || {},
target: this
};
};
/**
* A useful function to deal with inheritance. It will make the target
* inherit the prototype of the class dispatcher as well as its constructor.
*
* @param {object} target The target.
*/
dispatcher.extend = function(target, args) {
var k;
for (k in dispatcher.prototype)
if (dispatcher.prototype.hasOwnProperty(k))
target[k] = dispatcher.prototype[k];
dispatcher.apply(target, args);
};
/**
* EXPORT:
* *******
*/
if (typeof this.sigma !== 'undefined') {
this.sigma.classes = this.sigma.classes || {};
this.sigma.classes.dispatcher = dispatcher;
} else if (true) {
if (typeof module !== 'undefined' && module.exports)
exports = module.exports = dispatcher;
exports.dispatcher = dispatcher;
} else
this.dispatcher = dispatcher;
}).call(this);
}.call(window));
/***/ }),
/* 39 */
/***/ (function(module, exports, __webpack_require__) {
/*** IMPORTS FROM imports-loader ***/
(function() {
;(function(undefined) {
'use strict';
/**
* Sigma Quadtree Module for edges
* ===============================
*
* Author: Sébastien Heymann,
* from the quad of Guillaume Plique (Yomguithereal)
* Version: 0.2
*/
/**
* Quad Geometric Operations
* -------------------------
*
* A useful batch of geometric operations used by the quadtree.
*/
var _geom = {
/**
* Transforms a graph node with x, y and size into an
* axis-aligned square.
*
* @param {object} A graph node with at least a point (x, y) and a size.
* @return {object} A square: two points (x1, y1), (x2, y2) and height.
*/
pointToSquare: function(n) {
return {
x1: n.x - n.size,
y1: n.y - n.size,
x2: n.x + n.size,
y2: n.y - n.size,
height: n.size * 2
};
},
/**
* Transforms a graph edge with x1, y1, x2, y2 and size into an
* axis-aligned square.
*
* @param {object} A graph edge with at least two points
* (x1, y1), (x2, y2) and a size.
* @return {object} A square: two points (x1, y1), (x2, y2) and height.
*/
lineToSquare: function(e) {
if (e.y1 < e.y2) {
// (e.x1, e.y1) on top
if (e.x1 < e.x2) {
// (e.x1, e.y1) on left
return {
x1: e.x1 - e.size,
y1: e.y1 - e.size,
x2: e.x2 + e.size,
y2: e.y1 - e.size,
height: e.y2 - e.y1 + e.size * 2
};
}
// (e.x1, e.y1) on right
return {
x1: e.x2 - e.size,
y1: e.y1 - e.size,
x2: e.x1 + e.size,
y2: e.y1 - e.size,
height: e.y2 - e.y1 + e.size * 2
};
}
// (e.x2, e.y2) on top
if (e.x1 < e.x2) {
// (e.x1, e.y1) on left
return {
x1: e.x1 - e.size,
y1: e.y2 - e.size,
x2: e.x2 + e.size,
y2: e.y2 - e.size,
height: e.y1 - e.y2 + e.size * 2
};
}
// (e.x2, e.y2) on right
return {
x1: e.x2 - e.size,
y1: e.y2 - e.size,
x2: e.x1 + e.size,
y2: e.y2 - e.size,
height: e.y1 - e.y2 + e.size * 2
};
},
/**
* Transforms a graph edge of type 'curve' with x1, y1, x2, y2,
* control point and size into an axis-aligned square.
*
* @param {object} e A graph edge with at least two points
* (x1, y1), (x2, y2) and a size.
* @param {object} cp A control point (x,y).
* @return {object} A square: two points (x1, y1), (x2, y2) and height.
*/
quadraticCurveToSquare: function(e, cp) {
var pt = sigma.utils.getPointOnQuadraticCurve(
0.5,
e.x1,
e.y1,
e.x2,
e.y2,
cp.x,
cp.y
);
// Bounding box of the two points and the point at the middle of the
// curve:
var minX = Math.min(e.x1, e.x2, pt.x),
maxX = Math.max(e.x1, e.x2, pt.x),
minY = Math.min(e.y1, e.y2, pt.y),
maxY = Math.max(e.y1, e.y2, pt.y);
return {
x1: minX - e.size,
y1: minY - e.size,
x2: maxX + e.size,
y2: minY - e.size,
height: maxY - minY + e.size * 2
};
},
/**
* Transforms a graph self loop into an axis-aligned square.
*
* @param {object} n A graph node with a point (x, y) and a size.
* @return {object} A square: two points (x1, y1), (x2, y2) and height.
*/
selfLoopToSquare: function(n) {
// Fitting to the curve is too costly, we compute a larger bounding box
// using the control points:
var cp = sigma.utils.getSelfLoopControlPoints(n.x, n.y, n.size);
// Bounding box of the point and the two control points:
var minX = Math.min(n.x, cp.x1, cp.x2),
maxX = Math.max(n.x, cp.x1, cp.x2),
minY = Math.min(n.y, cp.y1, cp.y2),
maxY = Math.max(n.y, cp.y1, cp.y2);
return {
x1: minX - n.size,
y1: minY - n.size,
x2: maxX + n.size,
y2: minY - n.size,
height: maxY - minY + n.size * 2
};
},
/**
* Checks whether a rectangle is axis-aligned.
*
* @param {object} A rectangle defined by two points
* (x1, y1) and (x2, y2).
* @return {boolean} True if the rectangle is axis-aligned.
*/
isAxisAligned: function(r) {
return r.x1 === r.x2 || r.y1 === r.y2;
},
/**
* Compute top points of an axis-aligned rectangle. This is useful in
* cases when the rectangle has been rotated (left, right or bottom up) and
* later operations need to know the top points.
*
* @param {object} An axis-aligned rectangle defined by two points
* (x1, y1), (x2, y2) and height.
* @return {object} A rectangle: two points (x1, y1), (x2, y2) and height.
*/
axisAlignedTopPoints: function(r) {
// Basic
if (r.y1 === r.y2 && r.x1 < r.x2)
return r;
// Rotated to right
if (r.x1 === r.x2 && r.y2 > r.y1)
return {
x1: r.x1 - r.height, y1: r.y1,
x2: r.x1, y2: r.y1,
height: r.height
};
// Rotated to left
if (r.x1 === r.x2 && r.y2 < r.y1)
return {
x1: r.x1, y1: r.y2,
x2: r.x2 + r.height, y2: r.y2,
height: r.height
};
// Bottom's up
return {
x1: r.x2, y1: r.y1 - r.height,
x2: r.x1, y2: r.y1 - r.height,
height: r.height
};
},
/**
* Get coordinates of a rectangle's lower left corner from its top points.
*
* @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
* @return {object} Coordinates of the corner (x, y).
*/
lowerLeftCoor: function(r) {
var width = (
Math.sqrt(
Math.pow(r.x2 - r.x1, 2) +
Math.pow(r.y2 - r.y1, 2)
)
);
return {
x: r.x1 - (r.y2 - r.y1) * r.height / width,
y: r.y1 + (r.x2 - r.x1) * r.height / width
};
},
/**
* Get coordinates of a rectangle's lower right corner from its top points
* and its lower left corner.
*
* @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
* @param {object} A corner's coordinates (x, y).
* @return {object} Coordinates of the corner (x, y).
*/
lowerRightCoor: function(r, llc) {
return {
x: llc.x - r.x1 + r.x2,
y: llc.y - r.y1 + r.y2
};
},
/**
* Get the coordinates of all the corners of a rectangle from its top point.
*
* @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
* @return {array} An array of the four corners' coordinates (x, y).
*/
rectangleCorners: function(r) {
var llc = this.lowerLeftCoor(r),
lrc = this.lowerRightCoor(r, llc);
return [
{x: r.x1, y: r.y1},
{x: r.x2, y: r.y2},
{x: llc.x, y: llc.y},
{x: lrc.x, y: lrc.y}
];
},
/**
* Split a square defined by its boundaries into four.
*
* @param {object} Boundaries of the square (x, y, width, height).
* @return {array} An array containing the four new squares, themselves
* defined by an array of their four corners (x, y).
*/
splitSquare: function(b) {
return [
[
{x: b.x, y: b.y},
{x: b.x + b.width / 2, y: b.y},
{x: b.x, y: b.y + b.height / 2},
{x: b.x + b.width / 2, y: b.y + b.height / 2}
],
[
{x: b.x + b.width / 2, y: b.y},
{x: b.x + b.width, y: b.y},
{x: b.x + b.width / 2, y: b.y + b.height / 2},
{x: b.x + b.width, y: b.y + b.height / 2}
],
[
{x: b.x, y: b.y + b.height / 2},
{x: b.x + b.width / 2, y: b.y + b.height / 2},
{x: b.x, y: b.y + b.height},
{x: b.x + b.width / 2, y: b.y + b.height}
],
[
{x: b.x + b.width / 2, y: b.y + b.height / 2},
{x: b.x + b.width, y: b.y + b.height / 2},
{x: b.