sigma
Version:
A JavaScript library dedicated to graph drawing.
1,544 lines (1,432 loc) • 200 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["Sigma"] = factory();
else
root["Sigma"] = factory();
})(self, function() {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ([
/* 0 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Sigma = exports.MouseCaptor = exports.QuadTree = exports.Camera = void 0;
/**
* Sigma.js Library Endpoint
* ==========================
*
* The library endpoint.
* @module
*/
var camera_1 = __importDefault(__webpack_require__(1));
exports.Camera = camera_1.default;
var quadtree_1 = __importDefault(__webpack_require__(8));
exports.QuadTree = quadtree_1.default;
var mouse_1 = __importDefault(__webpack_require__(10));
exports.MouseCaptor = mouse_1.default;
var sigma_1 = __importDefault(__webpack_require__(12));
exports.Sigma = sigma_1.default;
/***/ }),
/* 1 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
/**
* Sigma.js Camera Class
* ======================
*
* Class designed to store camera information & used to update it.
* @module
*/
var events_1 = __webpack_require__(2);
var animate_1 = __webpack_require__(3);
var easings_1 = __importDefault(__webpack_require__(7));
var utils_1 = __webpack_require__(4);
/**
* Defaults.
*/
var DEFAULT_ZOOMING_RATIO = 1.5;
// TODO: animate options = number polymorphism?
// TODO: pan, zoom, unzoom, reset, rotate, zoomTo
// TODO: add width / height to camera and add #.resize
// TODO: bind camera to renderer rather than sigma
// TODO: add #.graphToDisplay, #.displayToGraph, batch methods later
/**
* Camera class
*
* @constructor
*/
var Camera = /** @class */ (function (_super) {
__extends(Camera, _super);
function Camera() {
var _this = _super.call(this) || this;
_this.x = 0.5;
_this.y = 0.5;
_this.angle = 0;
_this.ratio = 1;
_this.nextFrame = null;
_this.enabled = true;
// State
_this.previousState = _this.getState();
return _this;
}
/**
* Static method used to create a Camera object with a given state.
*
* @param state
* @return {Camera}
*/
Camera.from = function (state) {
var camera = new Camera();
return camera.setState(state);
};
/**
* Method used to enable the camera.
*
* @return {Camera}
*/
Camera.prototype.enable = function () {
this.enabled = true;
return this;
};
/**
* Method used to disable the camera.
*
* @return {Camera}
*/
Camera.prototype.disable = function () {
this.enabled = false;
return this;
};
/**
* Method used to retrieve the camera's current state.
*
* @return {object}
*/
Camera.prototype.getState = function () {
return {
x: this.x,
y: this.y,
angle: this.angle,
ratio: this.ratio,
};
};
/**
* Method used to retrieve the camera's previous state.
*
* @return {object}
*/
Camera.prototype.getPreviousState = function () {
var state = this.previousState;
return {
x: state.x,
y: state.y,
angle: state.angle,
ratio: state.ratio,
};
};
/**
* Method used to check whether the camera is currently being animated.
*
* @return {boolean}
*/
Camera.prototype.isAnimated = function () {
return !!this.nextFrame;
};
/**
* Method returning the coordinates of a point from the framed graph system to the
* viewport system.
*
* @param {object} dimensions - Dimensions of the viewport.
* @param {object} coordinates - Coordinates of the point.
* @return {object} - The point coordinates in the viewport.
*/
Camera.prototype.framedGraphToViewport = function (dimensions, coordinates) {
var smallestDimension = Math.min(dimensions.width, dimensions.height);
var dx = smallestDimension / dimensions.width;
var dy = smallestDimension / dimensions.height;
var ratio = this.ratio / smallestDimension;
// Align with center of the graph:
var x1 = (coordinates.x - this.x) / ratio;
var y1 = (this.y - coordinates.y) / ratio;
// Rotate:
var x2 = x1 * Math.cos(this.angle) - y1 * Math.sin(this.angle);
var y2 = y1 * Math.cos(this.angle) + x1 * Math.sin(this.angle);
return {
// Translate to center of screen
x: x2 + smallestDimension / 2 / dx,
y: y2 + smallestDimension / 2 / dy,
};
};
/**
* Method returning the coordinates of a point from the viewport system to the
* framed graph system.
*
* @param {object} dimensions - Dimensions of the viewport.
* @param {object} coordinates - Coordinates of the point.
* @return {object} - The point coordinates in the graph frame.
*/
Camera.prototype.viewportToFramedGraph = function (dimensions, coordinates) {
var _a;
var smallestDimension = Math.min(dimensions.width, dimensions.height);
var dx = smallestDimension / dimensions.width;
var dy = smallestDimension / dimensions.height;
var ratio = this.ratio / smallestDimension;
// Align with center of the graph:
var x = coordinates.x - smallestDimension / 2 / dx;
var y = coordinates.y - smallestDimension / 2 / dy;
// Rotate:
_a = __read([
x * Math.cos(-this.angle) - y * Math.sin(-this.angle),
y * Math.cos(-this.angle) + x * Math.sin(-this.angle),
], 2), x = _a[0], y = _a[1];
return {
x: x * ratio + this.x,
y: -y * ratio + this.y,
};
};
/**
* Method returning the abstract rectangle containing the graph according
* to the camera's state.
*
* @return {object} - The view's rectangle.
*/
Camera.prototype.viewRectangle = function (dimensions) {
// TODO: reduce relative margin?
var marginX = (0 * dimensions.width) / 8, marginY = (0 * dimensions.height) / 8;
var p1 = this.viewportToFramedGraph(dimensions, { x: 0 - marginX, y: 0 - marginY }), p2 = this.viewportToFramedGraph(dimensions, { x: dimensions.width + marginX, y: 0 - marginY }), h = this.viewportToFramedGraph(dimensions, { x: 0, y: dimensions.height + marginY });
return {
x1: p1.x,
y1: p1.y,
x2: p2.x,
y2: p2.y,
height: p2.y - h.y,
};
};
/**
* Method used to set the camera's state.
*
* @param {object} state - New state.
* @return {Camera}
*/
Camera.prototype.setState = function (state) {
if (!this.enabled)
return this;
// TODO: validations
// TODO: update by function
// Keeping track of last state
this.previousState = this.getState();
if (state.x)
this.x = state.x;
if (state.y)
this.y = state.y;
if (state.angle)
this.angle = state.angle;
if (state.ratio)
this.ratio = state.ratio;
// Emitting
// TODO: don't emit if nothing changed?
this.emit("updated", this.getState());
return this;
};
/**
* Method used to (un)zoom, while preserving the position of a viewport point.
* Used for instance to
*
* @param viewportTarget
* @param dimensions
* @param ratio
* @return {CameraState}
*/
Camera.prototype.getViewportZoomedState = function (viewportTarget, dimensions, ratio) {
// TODO: handle max zoom
var ratioDiff = ratio / this.ratio;
var center = {
x: dimensions.width / 2,
y: dimensions.height / 2,
};
var graphMousePosition = this.viewportToFramedGraph(dimensions, viewportTarget);
var graphCenterPosition = this.viewportToFramedGraph(dimensions, center);
return __assign(__assign({}, this.getState()), { x: (graphMousePosition.x - graphCenterPosition.x) * (1 - ratioDiff) + this.x, y: (graphMousePosition.y - graphCenterPosition.y) * (1 - ratioDiff) + this.y, ratio: ratio });
};
/**
* Method used to animate the camera.
*
* @param {object} state - State to reach eventually.
* @param {object} opts - Options:
* @param {number} duration - Duration of the animation.
* @param {string | number => number} easing - Easing function or name of an existing one
* @param {function} callback - Callback
*/
Camera.prototype.animate = function (state, opts, callback) {
var _this = this;
if (!this.enabled)
return;
var options = Object.assign({}, animate_1.ANIMATE_DEFAULTS, opts);
var easing = typeof options.easing === "function" ? options.easing : easings_1.default[options.easing];
// State
var start = Date.now(), initialState = this.getState();
// Function performing the animation
var fn = function () {
var t = (Date.now() - start) / options.duration;
// The animation is over:
if (t >= 1) {
_this.nextFrame = null;
_this.setState(state);
if (_this.animationCallback) {
_this.animationCallback.call(null);
_this.animationCallback = undefined;
}
return;
}
var coefficient = easing(t);
var newState = {};
if (state.x)
newState.x = initialState.x + (state.x - initialState.x) * coefficient;
if (state.y)
newState.y = initialState.y + (state.y - initialState.y) * coefficient;
if (state.angle)
newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient;
if (state.ratio)
newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient;
_this.setState(newState);
_this.nextFrame = utils_1.requestFrame(fn);
};
if (this.nextFrame) {
utils_1.cancelFrame(this.nextFrame);
if (this.animationCallback)
this.animationCallback.call(null);
this.nextFrame = utils_1.requestFrame(fn);
}
else {
fn();
}
this.animationCallback = callback;
};
/**
* Method used to zoom the camera.
*
* @param {number|object} factorOrOptions - Factor or options.
* @return {function}
*/
Camera.prototype.animatedZoom = function (factorOrOptions) {
if (!factorOrOptions) {
this.animate({ ratio: this.ratio / DEFAULT_ZOOMING_RATIO });
}
else {
if (typeof factorOrOptions === "number")
return this.animate({ ratio: this.ratio / factorOrOptions });
else
this.animate({
ratio: this.ratio / (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO),
}, factorOrOptions);
}
};
/**
* Method used to unzoom the camera.
*
* @param {number|object} factorOrOptions - Factor or options.
*/
Camera.prototype.animatedUnzoom = function (factorOrOptions) {
if (!factorOrOptions) {
this.animate({ ratio: this.ratio * DEFAULT_ZOOMING_RATIO });
}
else {
if (typeof factorOrOptions === "number")
return this.animate({ ratio: this.ratio * factorOrOptions });
else
this.animate({
ratio: this.ratio * (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO),
}, factorOrOptions);
}
};
/**
* Method used to reset the camera.
*
* @param {object} options - Options.
*/
Camera.prototype.animatedReset = function (options) {
this.animate({
x: 0.5,
y: 0.5,
ratio: 1,
angle: 0,
}, options);
};
/**
* Returns a new Camera instance, with the same state as the current camera.
*
* @return {Camera}
*/
Camera.prototype.copy = function () {
return Camera.from(this.getState());
};
return Camera;
}(events_1.EventEmitter));
exports.default = Camera;
/***/ }),
/* 2 */
/***/ ((module) => {
"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
}
var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
}
function EventEmitter() {
EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error');
var events = this._events;
if (events !== undefined)
doError = (doError && events.error === undefined);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event
}
// At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
err.context = er;
throw err; // Unhandled 'error' event
}
var handler = events[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === undefined)
return this;
list = events[type];
if (list === undefined)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (events === undefined)
return this;
// not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === undefined)
return [];
var evlistener = events[type];
if (evlistener === undefined)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events !== undefined) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener !== undefined) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function once(emitter, name) {
return new Promise(function (resolve, reject) {
function errorListener(err) {
emitter.removeListener(name, resolver);
reject(err);
}
function resolver() {
if (typeof emitter.removeListener === 'function') {
emitter.removeListener('error', errorListener);
}
resolve([].slice.call(arguments));
};
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
}
});
}
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
if (typeof emitter.on === 'function') {
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
}
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === 'function') {
if (flags.once) {
emitter.once(name, listener);
} else {
emitter.on(name, listener);
}
} else if (typeof emitter.addEventListener === 'function') {
// EventTarget does not have `error` event semantics like Node
// EventEmitters, we do not listen for `error` events here.
emitter.addEventListener(name, function wrapListener(arg) {
// IE does not have builtin `{ once: true }` support so we
// have to do it manually.
if (flags.once) {
emitter.removeEventListener(name, wrapListener);
}
listener(arg);
});
} else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
}
}
/***/ }),
/* 3 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.animateNodes = exports.ANIMATE_DEFAULTS = void 0;
var index_1 = __webpack_require__(4);
var easings_1 = __importDefault(__webpack_require__(7));
exports.ANIMATE_DEFAULTS = {
easing: "quadraticInOut",
duration: 150,
};
/**
* Function used to animate the nodes.
*/
function animateNodes(graph, targets, opts, callback) {
var options = Object.assign({}, exports.ANIMATE_DEFAULTS, opts);
var easing = typeof options.easing === "function" ? options.easing : easings_1.default[options.easing];
var start = Date.now();
var startPositions = {};
for (var node in targets) {
var attrs = targets[node];
startPositions[node] = {};
for (var k in attrs)
startPositions[node][k] = graph.getNodeAttribute(node, k);
}
var frame = null;
var step = function () {
var p = (Date.now() - start) / options.duration;
if (p >= 1) {
// Animation is done
for (var node in targets) {
var attrs = targets[node];
for (var k in attrs)
graph.setNodeAttribute(node, k, attrs[k]);
}
if (typeof callback === "function")
callback();
return;
}
p = easing(p);
for (var node in targets) {
var attrs = targets[node];
var s = startPositions[node];
for (var k in attrs)
graph.setNodeAttribute(node, k, attrs[k] * p + s[k] * (1 - p));
}
frame = index_1.requestFrame(step);
};
step();
return function () {
if (frame)
index_1.cancelFrame(frame);
};
}
exports.animateNodes = animateNodes;
/***/ }),
/* 4 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.validateGraph = exports.canUse32BitsIndices = exports.extractPixel = exports.matrixFromCamera = exports.floatColor = exports.zIndexOrdering = exports.createNormalizationFunction = exports.getPixelRatio = exports.createElement = exports.cancelFrame = exports.requestFrame = exports.assignDeep = exports.isPlainObject = void 0;
var is_graph_1 = __importDefault(__webpack_require__(5));
var matrices_1 = __webpack_require__(6);
/**
* Checks whether the given value is a plain object.
*
* @param {mixed} value - Target value.
* @return {boolean}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
function isPlainObject(value) {
return typeof value === "object" && value !== null && value.constructor === Object;
}
exports.isPlainObject = isPlainObject;
/**
* Very simple recursive Object.assign-like function.
*
* @param {object} target - First object.
* @param {object} [...objects] - Objects to merge.
* @return {object}
*/
function assignDeep(target) {
var objects = [];
for (var _i = 1; _i < arguments.length; _i++) {
objects[_i - 1] = arguments[_i];
}
target = target || {};
for (var i = 0, l = objects.length; i < l; i++) {
var o = objects[i];
if (!o)
continue;
for (var k in o) {
if (isPlainObject(o[k])) {
target[k] = assignDeep(target[k], o[k]);
}
else {
target[k] = o[k];
}
}
}
return target;
}
exports.assignDeep = assignDeep;
/**
* Just some dirty trick to make requestAnimationFrame and cancelAnimationFrame "work" in Node.js, for unit tests:
*/
exports.requestFrame = typeof requestAnimationFrame !== "undefined"
? function (callback) { return requestAnimationFrame(callback); }
: function (callback) { return setTimeout(callback, 0); };
exports.cancelFrame = typeof cancelAnimationFrame !== "undefined"
? function (requestID) { return cancelAnimationFrame(requestID); }
: function (requestID) { return clearTimeout(requestID); };
/**
* Function used to create DOM elements easily.
*
* @param {string} tag - Tag name of the element to create.
* @param {object} style - Styles map.
* @param {object} attributes - Attributes map.
* @return {HTMLElement}
*/
function createElement(tag, style, attributes) {
var element = document.createElement(tag);
if (style) {
for (var k in style) {
element.style[k] = style[k];
}
}
if (attributes) {
for (var k in attributes) {
element.setAttribute(k, attributes[k]);
}
}
return element;
}
exports.createElement = createElement;
/**
* Function returning the browser's pixel ratio.
*
* @return {number}
*/
function getPixelRatio() {
if (typeof window.devicePixelRatio !== "undefined")
return window.devicePixelRatio;
return 1;
}
exports.getPixelRatio = getPixelRatio;
function createNormalizationFunction(extent) {
var _a = __read(extent.x, 2), minX = _a[0], maxX = _a[1], _b = __read(extent.y, 2), minY = _b[0], maxY = _b[1];
var ratio = Math.max(maxX - minX, maxY - minY);
if (ratio === 0)
ratio = 1;
var dX = (maxX + minX) / 2, dY = (maxY + minY) / 2;
var fn = function (data) {
return {
x: 0.5 + (data.x - dX) / ratio,
y: 0.5 + (data.y - dY) / ratio,
};
};
// TODO: possibility to apply this in batch over array of indices
fn.applyTo = function (data) {
data.x = 0.5 + (data.x - dX) / ratio;
data.y = 0.5 + (data.y - dY) / ratio;
};
fn.inverse = function (data) {
return {
x: dX + ratio * (data.x - 0.5),
y: dY + ratio * (data.y - 0.5),
};
};
fn.ratio = ratio;
return fn;
}
exports.createNormalizationFunction = createNormalizationFunction;
/**
* Function ordering the given elements in reverse z-order so they drawn
* the correct way.
*
* @param {number} extent - [min, max] z values.
* @param {function} getter - Z attribute getter function.
* @param {array} elements - The array to sort.
* @return {array} - The sorted array.
*/
function zIndexOrdering(extent, getter, elements) {
// If k is > n, we'll use a standard sort
return elements.sort(function (a, b) {
var zA = getter(a) || 0, zB = getter(b) || 0;
if (zA < zB)
return -1;
if (zA > zB)
return 1;
return 0;
});
// TODO: counting sort optimization
}
exports.zIndexOrdering = zIndexOrdering;
/**
* WebGL utils
* ===========
*/
/**
* Memoized function returning a float-encoded color from various string
* formats describing colors.
*/
var FLOAT_COLOR_CACHE = {};
var INT8 = new Int8Array(4);
var INT32 = new Int32Array(INT8.buffer, 0, 1);
var FLOAT32 = new Float32Array(INT8.buffer, 0, 1);
var RGBA_TEST_REGEX = /^\s*rgba?\s*\(/;
var RGBA_EXTRACT_REGEX = /^\s*rgba?\s*\(\s*([0-9]*)\s*,\s*([0-9]*)\s*,\s*([0-9]*)(?:\s*,\s*(.*)?)?\)\s*$/;
function floatColor(val) {
// If the color is already computed, we yield it
if (typeof FLOAT_COLOR_CACHE[val] !== "undefined")
return FLOAT_COLOR_CACHE[val];
var r = 0, g = 0, b = 0, a = 1;
// Handling hexadecimal notation
if (val[0] === "#") {
if (val.length === 4) {
r = parseInt(val.charAt(1) + val.charAt(1), 16);
g = parseInt(val.charAt(2) + val.charAt(2), 16);
b = parseInt(val.charAt(3) + val.charAt(3), 16);
}
else {
r = parseInt(val.charAt(1) + val.charAt(2), 16);
g = parseInt(val.charAt(3) + val.charAt(4), 16);
b = parseInt(val.charAt(5) + val.charAt(6), 16);
}
}
// Handling rgb notation
else if (RGBA_TEST_REGEX.test(val)) {
var match = val.match(RGBA_EXTRACT_REGEX);
if (match) {
r = +match[1];
g = +match[2];
b = +match[3];
if (match[4])
a = +match[4];
}
}
a = (a * 255) | 0;
INT32[0] = ((a << 24) | (b << 16) | (g << 8) | r) & 0xfeffffff;
var color = FLOAT32[0];
FLOAT_COLOR_CACHE[val] = color;
return color;
}
exports.floatColor = floatColor;
/**
* Function returning a matrix from the current state of the camera.
*/
// TODO: it's possible to optimize this drastically!
function matrixFromCamera(state, dimensions) {
var angle = state.angle, ratio = state.ratio, x = state.x, y = state.y;
var width = dimensions.width, height = dimensions.height;
var matrix = matrices_1.identity();
var smallestDimension = Math.min(width, height);
var cameraCentering = matrices_1.translate(matrices_1.identity(), -x, -y), cameraScaling = matrices_1.scale(matrices_1.identity(), 1 / ratio), cameraRotation = matrices_1.rotate(matrices_1.identity(), -angle), viewportScaling = matrices_1.scale(matrices_1.identity(), 2 * (smallestDimension / width), 2 * (smallestDimension / height));
// Logical order is reversed
matrices_1.multiply(matrix, viewportScaling);
matrices_1.multiply(matrix, cameraRotation);
matrices_1.multiply(matrix, cameraScaling);
matrices_1.multiply(matrix, cameraCentering);
return matrix;
}
exports.matrixFromCamera = matrixFromCamera;
/**
* Function extracting the color at the given pixel.
*/
function extractPixel(gl, x, y, array) {
var data = array || new Uint8Array(4);
gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, data);
return data;
}
exports.extractPixel = extractPixel;
/**
* Function used to know whether given webgl context can use 32 bits indices.
*/
function canUse32BitsIndices(gl) {
var webgl2 = typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext;
return webgl2 || !!gl.getExtension("OES_element_index_uint");
}
exports.canUse32BitsIndices = canUse32BitsIndices;
/**
* Check if the graph variable is a valid graph, and if sigma can render it.
*/
function validateGraph(graph) {
// check if it's a valid graphology instance
if (!is_graph_1.default(graph))
throw new Error("Sigma: invalid graph instance.");
// check if nodes have x/y attributes
graph.forEachNode(function (key, attributes) {
if (!Number.isFinite(attributes.x) || !Number.isFinite(attributes.y)) {
throw new Error("Sigma: Coordinates of node " + key + " are invalid. A node must have a numeric 'x' and 'y' attribute.");
}
});
}
exports.validateGraph = validateGraph;
/***/ }),
/* 5 */
/***/ ((module) => {
/**
* Graphology isGraph
* ===================
*
* Very simple function aiming at ensuring the given variable is a
* graphology instance.
*/
/**
* Checking the value is a graphology instance.
*
* @param {any} value - Target value.
* @return {boolean}
*/
module.exports = function isGraph(value) {
return (
value !== null &&
typeof value === 'object' &&
typeof value.addUndirectedEdgeWithKey === 'function' &&
typeof value.dropNode === 'function' &&
typeof value.multi === 'boolean'
);
};
/***/ }),
/* 6 */
/***/ ((__unused_webpack_module, exports) => {
"use strict";
/**
* Sigma.js WebGL Matrices Helpers
* ================================
*
* Matrices-related helper functions used by sigma's WebGL renderer.
* @module
*/
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.multiply = exports.translate = exports.rotate = exports.scale = exports.identity = void 0;
function identity() {
return Float32Array.of(1, 0, 0, 0, 1, 0, 0, 0, 1);
}
exports.identity = identity;
// TODO: optimize
function scale(m, x, y) {
m[0] = x;
m[4] = typeof y === "number" ? y : x;
return m;
}
exports.scale = scale;
function rotate(m, r) {
var s = Math.sin(r), c = Math.cos(r);
m[0] = c;
m[1] = s;
m[3] = -s;
m[4] = c;
return m;
}
exports.rotate = rotate;
function translate(m, x, y) {
m[6] = x;
m[7] = y;
return m;
}
exports.translate = translate;
function multiply(a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2];
var a10 = a[3], a11 = a[4], a12 = a[5];
var a20 = a[6], a21 = a[7], a22 = a[8];
var b00 = b[0], b01 = b[1], b02 = b[2];
var b10 = b[3], b11 = b[4], b12 = b[5];
var b20 = b[6], b21 = b[7], b22 = b[8];
a[0] = b00 * a00 + b01 * a10 + b02 * a20;
a[1] = b00 * a01 + b01 * a11 + b02 * a21;
a[2] = b00 * a02 + b01 * a12 + b02 * a22;
a[3] = b10 * a00 + b11 * a10 + b12 * a20;
a[4] = b10 * a01 + b11 * a11 + b12 * a21;
a[5] = b10 * a02 + b11 * a12 + b12 * a22;
a[6] = b20 * a00 + b21 * a10 + b22 * a20;
a[7] = b20 * a01 + b21 * a11 + b22 * a21;
a[8] = b20 * a02 + b21 * a12 + b22 * a22;
return a;
}
exports.multiply = multiply;
/***/ }),
/* 7 */
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.cubicInOut = exports.cubicOut = exports.cubicIn = exports.quadraticInOut = exports.quadraticOut = exports.quadraticIn = exports.linear = void 0;
/**
* Sigma.js Easings
* =================
*
* Handy collection of easing functions.
* @module
*/
var linear = function (k) { return k; };
exports.linear = linear;
var quadraticIn = function (k) { return k * k; };
exports.quadraticIn = quadraticIn;
var quadraticOut = function (k) { return k * (2 - k); };
exports.quadraticOut = quadraticOut;
var quadraticInOut = function (k) {
if ((k *= 2) < 1)
return 0.5 * k * k;
return -0.5 * (--k * (k - 2) - 1);
};
exports.quadraticInOut = quadraticInOut;
var cubicIn = function (k) { return k * k * k; };
exports.cubicIn = cubicIn;
var cubicOut = function (k) { return --k * k * k + 1; };
exports.cubicOut = cubicOut;
var cubicInOut = function (k) {
if ((k *= 2) < 1)
return 0.5 * k * k * k;
return 0.5 * ((k -= 2) * k * k + 2);
};
exports.cubicInOut = cubicInOut;
var easings = {
linear: exports.linear,
quadraticIn: exports.quadraticIn,
quadraticOut: exports.quadraticOut,
quadraticInOut: exports.quadraticInOut,
cubicIn: exports.cubicIn,
cubicOut: exports.cubicOut,
cubicInOut: exports.cubicInOut,
};
exports.default = easings;
/***/ }),
/* 8 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.rectangleCollidesWithQuad = exports.squareCollidesWithQuad = exports.getCircumscribedAlignedRectangle = exports.isRectangleAligned = void 0;
/**
* Sigma.js Quad Tree Class
* =========================
*
* Class implementing the quad tree data structure used to solve hovers and
* determine which elements are currently in the scope of the camera so that
* we don't waste time rendering things the user cannot see anyway.
* @module
*/
/* eslint no-nested-ternary: 0 */
/* eslint no-constant-condition: 0 */
var extend_1 = __importDefault(__webpack_require__(9));
// TODO: should not ask the quadtree when the camera has the whole graph in
// sight.
// TODO: a square can be represented as topleft + width, saying for the quad blocks (reduce mem)
// TODO: jsdoc
// TODO: be sure we can handle cases overcoming boundaries (because of size) or use a maxed size
// TODO: filtering unwanted labels beforehand through the filter function
// NOTE: this is basically a MX-CIF Quadtree at this point
// NOTE: need to explore R-Trees for edges
// NOTE: need to explore 2d segment tree for edges
// NOTE: probably can do faster using spatial hashing
/**
* Constants.
*
* Note that since we are representing a static 4-ary tree, the indices of the
* quadrants are the following:
* - TOP_LEFT: 4i + b
* - TOP_RIGHT: 4i + 2b
* - BOTTOM_LEFT: 4i + 3b
* - BOTTOM_RIGHT: 4i + 4b
*/
var BLOCKS = 4, MAX_LEVEL = 5;
var X_OFFSET = 0, Y_OFFSET = 1, WIDTH_OFFSET = 2, HEIGHT_OFFSET = 3;
var TOP_LEFT = 1, TOP_RIGHT = 2, BOTTOM_LEFT = 3, BOTTOM_RIGHT = 4;
/**
* Geometry helpers.
*/
/**
* Function returning whether the given rectangle is axis-aligned.
*
* @param {Rectangle} rect
* @return {boolean}
*/
function isRectangleAligned(rect) {
return rect.x1 === rect.x2 || rect.y1 === rect.y2;
}
exports.isRectangleAligned = isRectangleAligned;
/**
* Function returning the smallest rectangle that contains the given rectangle, and that is aligned with the axis.
*
* @param {Rectangle} rect
* @return {Rectangle}
*/
function getCircumscribedAlignedRectangle(rect) {
var width = Math.sqrt(Math.pow(rect.x2 - rect.x1, 2) + Math.pow(rect.y2 - rect.y1, 2));
var heightVector = {
x: ((rect.y1 - rect.y2) * rect.height) / width,
y: ((rect.x2 - rect.x1) * rect.height) / width,
};
// Compute all corners:
var tl = { x: rect.x1, y: rect.y1 };
var tr = { x: rect.x2, y: rect.y2 };
var bl = {
x: rect.x1 + heightVector.x,
y: rect.y1 + heightVector.y,
};
var br = {
x: rect.x2 + heightVector.x,
y: rect.y2 + heightVector.y,
};
var xL = Math.min(tl.x, tr.x, bl.x, br.x);
var xR = Math.max(tl.x, tr.x, bl.x, br.x);
var yT = Math.min(tl.y, tr.y, bl.y, br.y);
var yB = Math.max(tl.y, tr.y, bl.y, br.y);
return {
x1: xL,
y1: yT,
x2: xR,
y2: yT,
height: yB - yT,
};
}
exports.getCircumscribedAlignedRectangle = getCircumscribedAlignedRectangle;
/**
*
* @param x1
* @param y1
* @param w
* @param qx
* @param qy
* @param qw
* @param qh
*/
function squareCollidesWithQuad(x1, y1, w, qx, qy, qw, qh) {
return x1 < qx + qw && x1 + w > qx && y1 < qy + qh && y1 + w > qy;
}
exports.squareCollidesWithQuad = squareCollidesWithQuad;
function rectangleCollidesWithQuad(x1, y1, w, h, qx, qy, qw, qh) {
return x1 < qx + qw && x1 + w > qx && y1 < qy + qh && y1 + h > qy;
}
exports.rectangleCollidesWithQuad = rectangleCollidesWithQuad;
function pointIsInQuad(x, y, qx, qy, qw, qh) {
var xmp = qx + qw / 2, ymp = qy + qh / 2, top = y < ymp, left = x < xmp;
return top ? (left ? TOP_LEFT : TOP_RIGHT) : left ? BOTTOM_LEFT : BOTTOM_RIGHT;
}
/**
* Helper functions that are not bound to the class so an external user
* cannot mess with them.
*/
function buildQuadrants(maxLevel, data) {
// [block, level]
var stack = [0, 0];
while (stack.length) {
var level = stack.pop(), block = stack.pop();
var topLeftBlock = 4 * block + BLOCKS, topRightBlock = 4 * block + 2 * BLOC