Comparing version 2.0.0-alpha4 to 2.0.0-alpha40
559
camera.js
@@ -1,47 +0,52 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _events = require('events'); | ||
var _easings = require('./easings'); | ||
var easings = _interopRequireWildcard(_easings); | ||
var _utils = require('./utils'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js Camera Class | ||
* ====================== | ||
* | ||
* Class designed to store camera information & used to update it. | ||
*/ | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js Camera Class | ||
* ====================== | ||
* | ||
* Class designed to store camera information & used to update it. | ||
*/ | ||
var events_1 = require("events"); | ||
var easings = __importStar(require("./easings")); | ||
var utils_1 = require("./utils"); | ||
/** | ||
* Defaults. | ||
*/ | ||
var ANIMATE_DEFAULTS = { | ||
easing: 'quadraticInOut', | ||
duration: 150 | ||
easing: "quadraticInOut", | ||
duration: 150, | ||
}; | ||
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 | ||
/** | ||
@@ -52,42 +57,26 @@ * Camera class | ||
*/ | ||
var Camera = function (_EventEmitter) { | ||
_inherits(Camera, _EventEmitter); | ||
function Camera(dimensions) { | ||
_classCallCheck(this, Camera); | ||
dimensions = dimensions || {}; | ||
// Properties | ||
var _this = _possibleConstructorReturn(this, (Camera.__proto__ || Object.getPrototypeOf(Camera)).call(this)); | ||
_this.x = 0; | ||
_this.y = 0; | ||
_this.angle = 0; | ||
_this.ratio = 1; | ||
_this.width = dimensions.width || 0; | ||
_this.height = dimensions.height || 0; | ||
// State | ||
_this.nextFrame = null; | ||
_this.enabled = true; | ||
return _this; | ||
} | ||
/** | ||
* Method used to enable the camera. | ||
* | ||
* @return {Camera} | ||
*/ | ||
_createClass(Camera, [{ | ||
key: 'enable', | ||
value: function enable() { | ||
this.enabled = true; | ||
return this; | ||
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; | ||
} | ||
/** | ||
* Method used to enable the camera. | ||
* | ||
* @return {Camera} | ||
*/ | ||
Camera.prototype.enable = function () { | ||
this.enabled = true; | ||
return this; | ||
}; | ||
/** | ||
* Method used to disable the camera. | ||
@@ -97,10 +86,6 @@ * | ||
*/ | ||
}, { | ||
key: 'disable', | ||
value: function disable() { | ||
this.enabled = false; | ||
return this; | ||
} | ||
Camera.prototype.disable = function () { | ||
this.enabled = false; | ||
return this; | ||
}; | ||
/** | ||
@@ -111,29 +96,24 @@ * Method used to retrieve the camera's current state. | ||
*/ | ||
}, { | ||
key: 'getState', | ||
value: function getState() { | ||
return { | ||
x: this.x, | ||
y: this.y, | ||
angle: this.angle, | ||
ratio: this.ratio | ||
}; | ||
} | ||
Camera.prototype.getState = function () { | ||
return { | ||
x: this.x, | ||
y: this.y, | ||
angle: this.angle, | ||
ratio: this.ratio, | ||
}; | ||
}; | ||
/** | ||
* Method used to retrieve the camera's dimensions. | ||
* Method used to retrieve the camera's previous state. | ||
* | ||
* @return {object} | ||
*/ | ||
}, { | ||
key: 'getDimensions', | ||
value: function getDimensions() { | ||
return { | ||
width: this.width, | ||
height: this.height | ||
}; | ||
} | ||
Camera.prototype.getPreviousState = function () { | ||
var state = this.previousState; | ||
return { | ||
x: state.x, | ||
y: state.y, | ||
angle: state.angle, | ||
ratio: state.ratio, | ||
}; | ||
}; | ||
/** | ||
@@ -144,80 +124,63 @@ * Method used to check whether the camera is currently being animated. | ||
*/ | ||
}, { | ||
key: 'isAnimated', | ||
value: function isAnimated() { | ||
return !!this.nextFrame; | ||
} | ||
Camera.prototype.isAnimated = function () { | ||
return !!this.nextFrame; | ||
}; | ||
/** | ||
* Method returning the coordinates of a point from the display frame to the | ||
* graph one but ignores the offset and dimensions of the container. | ||
* Method returning the coordinates of a point from the graph frame to the | ||
* viewport. | ||
* | ||
* @param {number} x The X coordinate. | ||
* @param {number} y The Y coordinate. | ||
* @return {object} The point coordinates in the frame of the graph. | ||
* @param {object} dimensions - Dimensions of the viewport. | ||
* @param {number} x - The X coordinate. | ||
* @param {number} y - The Y coordinate. | ||
* @return {object} - The point coordinates in the viewport. | ||
*/ | ||
}, { | ||
key: 'abstractDisplayToGraph', | ||
value: function abstractDisplayToGraph(x, y) { | ||
var cos = Math.cos(this.angle), | ||
sin = Math.sin(this.angle); | ||
return { | ||
x: (x * cos - y * sin) * this.ratio, | ||
y: (y * cos + x * sin) * this.ratio | ||
}; | ||
} | ||
// TODO: assign to gain one object | ||
// TODO: angles | ||
Camera.prototype.graphToViewport = function (dimensions, x, y) { | ||
var smallestDimension = Math.min(dimensions.width, dimensions.height); | ||
var dx = smallestDimension / dimensions.width, dy = smallestDimension / dimensions.height; | ||
// TODO: we keep on the upper left corner! | ||
// TODO: how to normalize sizes? | ||
return { | ||
x: (x - this.x + this.ratio / 2 / dx) * (smallestDimension / this.ratio), | ||
y: (this.y - y + this.ratio / 2 / dy) * (smallestDimension / this.ratio), | ||
}; | ||
}; | ||
/** | ||
* Method returning the coordinates of a point from the display frame to the | ||
* graph one. | ||
* Method returning the coordinates of a point from the viewport frame to the | ||
* graph frame. | ||
* | ||
* @param {number} x The X coordinate. | ||
* @param {number} y The Y coordinate. | ||
* @return {object} The point coordinates in the frame of the graph. | ||
* @param {object} dimensions - Dimensions of the viewport. | ||
* @param {number} x - The X coordinate. | ||
* @param {number} y - The Y coordinate. | ||
* @return {object} - The point coordinates in the graph frame. | ||
*/ | ||
}, { | ||
key: 'displayToGraph', | ||
value: function displayToGraph(x, y) { | ||
var cos = Math.cos(this.angle), | ||
sin = Math.sin(this.angle); | ||
var xOffset = this.width / 2 - (this.x * cos + this.y * sin) / this.ratio, | ||
yOffset = this.height / 2 - (this.y * cos - this.x * sin) / this.ratio; | ||
var X = x - xOffset, | ||
Y = y - yOffset; | ||
return { | ||
x: (X * cos - Y * sin) * this.ratio, | ||
y: (Y * cos + X * sin) * this.ratio | ||
}; | ||
} | ||
// TODO: angles | ||
Camera.prototype.viewportToGraph = function (dimensions, x, y) { | ||
var smallestDimension = Math.min(dimensions.width, dimensions.height); | ||
var dx = smallestDimension / dimensions.width, dy = smallestDimension / dimensions.height; | ||
return { | ||
x: (this.ratio / smallestDimension) * x + this.x - this.ratio / 2 / dx, | ||
y: -((this.ratio / smallestDimension) * y - this.y - this.ratio / 2 / dy), | ||
}; | ||
}; | ||
/** | ||
* Method returning the coordinates of a point from the graph frame to the | ||
* display one. | ||
* Method returning the abstract rectangle containing the graph according | ||
* to the camera's state. | ||
* | ||
* @param {number} x The X coordinate. | ||
* @param {number} y The Y coordinate. | ||
* @return {object} The point coordinates in the frame of the display. | ||
* @return {object} - The view's rectangle. | ||
*/ | ||
}, { | ||
key: 'graphToDisplay', | ||
value: function graphToDisplay(x, y) { | ||
var relCos = Math.cos(this.angle) / this.ratio, | ||
relSin = Math.sin(this.angle) / this.ratio, | ||
xOffset = this.width / 2 - this.x * relCos - this.y * relSin, | ||
yOffset = this.height / 2 - this.y * relCos + this.x * relSin; | ||
return { | ||
x: x * relCos + y * relSin + xOffset, | ||
y: y * relCos - x * relSin + yOffset | ||
}; | ||
} | ||
// TODO: angle | ||
Camera.prototype.viewRectangle = function (dimensions) { | ||
// TODO: reduce relative margin? | ||
var marginX = (0 * dimensions.width) / 8, marginY = (0 * dimensions.height) / 8; | ||
var p1 = this.viewportToGraph(dimensions, 0 - marginX, 0 - marginY), p2 = this.viewportToGraph(dimensions, dimensions.width + marginX, 0 - marginY), h = this.viewportToGraph(dimensions, 0, dimensions.height + marginY); | ||
return { | ||
x1: p1.x, | ||
y1: p1.y, | ||
x2: p2.x, | ||
y2: p2.y, | ||
height: p2.y - h.y, | ||
}; | ||
}; | ||
/** | ||
@@ -229,50 +192,23 @@ * Method used to set the camera's state. | ||
*/ | ||
}, { | ||
key: 'setState', | ||
value: function setState(state) { | ||
if (!this.enabled) return this; | ||
// TODO: validations | ||
// TODO: update by function | ||
if ('x' in state) this.x = state.x; | ||
if ('y' in state) this.y = state.y; | ||
if ('angle' in state) this.angle = state.angle; | ||
if ('ratio' in state) this.ratio = state.ratio; | ||
// Emitting | ||
// TODO: don't emit if nothing changed? | ||
this.emit('updated', this.getState()); | ||
return this; | ||
} | ||
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 ("x" in state) | ||
this.x = state.x; | ||
if ("y" in state) | ||
this.y = state.y; | ||
if ("angle" in state) | ||
this.angle = state.angle; | ||
if ("ratio" in state) | ||
this.ratio = state.ratio; | ||
// Emitting | ||
// TODO: don't emit if nothing changed? | ||
this.emit("updated", this.getState()); | ||
return this; | ||
}; | ||
/** | ||
* Method used to resize the camera's dimensions. | ||
* | ||
* @param {object} dimensions - New dimensions. | ||
* @return {Camera} | ||
*/ | ||
}, { | ||
key: 'resize', | ||
value: function resize(dimensions) { | ||
if (!this.enabled) return this; | ||
if ('width' in dimensions) this.width = dimensions.width; | ||
if ('height' in dimensions) this.height = dimensions.height; | ||
this.emit('resized', this.getDimensions()); | ||
return this; | ||
} | ||
/** | ||
* Method used to animate the camera. | ||
@@ -286,57 +222,46 @@ * | ||
*/ | ||
}, { | ||
key: 'animate', | ||
value: function animate(state, options /*, callback */) { | ||
var _this2 = this; | ||
if (!this.enabled) return this; | ||
// TODO: validation | ||
options = (0, _utils.assign)({}, ANIMATE_DEFAULTS, options); | ||
var easing = typeof options.easing === 'function' ? options.easing : easings[options.easing]; | ||
// Canceling previous animation if needed | ||
if (this.nextFrame) cancelAnimationFrame(this.nextFrame); | ||
// State | ||
var start = Date.now(), | ||
initialState = this.getState(); | ||
// Function performing the animation | ||
var fn = function fn() { | ||
var t = (Date.now() - start) / options.duration; | ||
// The animation is over: | ||
if (t >= 1) { | ||
_this2.nextFrame = null; | ||
_this2.setState(state); | ||
return; | ||
Camera.prototype.animate = function (state, options, callback) { | ||
var _this = this; | ||
if (!this.enabled) | ||
return this; | ||
// TODO: validation | ||
options = utils_1.assign({}, ANIMATE_DEFAULTS, options); | ||
var easing = typeof options.easing === "function" ? options.easing : easings[options.easing]; | ||
// Canceling previous animation if needed | ||
if (this.nextFrame) | ||
cancelAnimationFrame(this.nextFrame); | ||
// 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 (typeof callback === "function") | ||
callback(); | ||
return; | ||
} | ||
var coefficient = easing(t); | ||
var newState = {}; | ||
if ("x" in state) | ||
newState.x = initialState.x + (state.x - initialState.x) * coefficient; | ||
if ("y" in state) | ||
newState.y = initialState.y + (state.y - initialState.y) * coefficient; | ||
if ("angle" in state) | ||
newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient; | ||
if ("ratio" in state) | ||
newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient; | ||
_this.setState(newState); | ||
_this.nextFrame = requestAnimationFrame(fn); | ||
}; | ||
if (this.nextFrame) { | ||
cancelAnimationFrame(this.nextFrame); | ||
this.nextFrame = requestAnimationFrame(fn); | ||
} | ||
var coefficient = easing(t); | ||
var newState = {}; | ||
if ('x' in state) newState.x = initialState.x + (state.x - initialState.x) * coefficient; | ||
if ('y' in state) newState.y = initialState.y + (state.y - initialState.y) * coefficient; | ||
if ('angle' in state) newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient; | ||
if ('ratio' in state) newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient; | ||
_this2.setState(newState); | ||
_this2.nextFrame = requestAnimationFrame(fn); | ||
}; | ||
if (this.nextFrame) { | ||
cancelAnimationFrame(this.nextFrame); | ||
this.nextFrame = requestAnimationFrame(fn); | ||
} else { | ||
fn(); | ||
} | ||
} | ||
else { | ||
fn(); | ||
} | ||
}; | ||
/** | ||
@@ -348,14 +273,15 @@ * Method used to zoom the camera. | ||
*/ | ||
}, { | ||
key: 'animatedZoom', | ||
value: function animatedZoom(factorOrOptions) { | ||
if (!factorOrOptions) { | ||
return this.animate({ ratio: this.ratio / DEFAULT_ZOOMING_RATIO }); | ||
} else { | ||
if (typeof factorOrOptions === 'number') return this.animate({ ratio: this.ratio / factorOrOptions });else return this.animate({ ratio: this.ratio / (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO) }, factorOrOptions); | ||
} | ||
} | ||
Camera.prototype.animatedZoom = function (factorOrOptions) { | ||
if (!factorOrOptions) { | ||
return this.animate({ ratio: this.ratio / DEFAULT_ZOOMING_RATIO }); | ||
} | ||
else { | ||
if (typeof factorOrOptions === "number") | ||
return this.animate({ ratio: this.ratio / factorOrOptions }); | ||
else | ||
return this.animate({ | ||
ratio: this.ratio / (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO), | ||
}, factorOrOptions); | ||
} | ||
}; | ||
/** | ||
@@ -367,14 +293,15 @@ * Method used to unzoom the camera. | ||
*/ | ||
}, { | ||
key: 'animatedUnzoom', | ||
value: function animatedUnzoom(factorOrOptions) { | ||
if (!factorOrOptions) { | ||
return this.animate({ ratio: this.ratio * DEFAULT_ZOOMING_RATIO }); | ||
} else { | ||
if (typeof factorOrOptions === 'number') return this.animate({ ratio: this.ratio * factorOrOptions });else return this.animate({ ratio: this.ratio * (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO) }, factorOrOptions); | ||
} | ||
} | ||
Camera.prototype.animatedUnzoom = function (factorOrOptions) { | ||
if (!factorOrOptions) { | ||
return this.animate({ ratio: this.ratio * DEFAULT_ZOOMING_RATIO }); | ||
} | ||
else { | ||
if (typeof factorOrOptions === "number") | ||
return this.animate({ ratio: this.ratio * factorOrOptions }); | ||
else | ||
return this.animate({ | ||
ratio: this.ratio * (factorOrOptions.factor || DEFAULT_ZOOMING_RATIO), | ||
}, factorOrOptions); | ||
} | ||
}; | ||
/** | ||
@@ -386,18 +313,12 @@ * Method used to reset the camera. | ||
*/ | ||
}, { | ||
key: 'animatedReset', | ||
value: function animatedReset(options) { | ||
return this.animate({ | ||
x: 0, | ||
y: 0, | ||
ratio: 1, | ||
angle: 0 | ||
}, options); | ||
} | ||
}]); | ||
return Camera; | ||
}(_events.EventEmitter); | ||
exports.default = Camera; | ||
Camera.prototype.animatedReset = function (options) { | ||
return this.animate({ | ||
x: 0.5, | ||
y: 0.5, | ||
ratio: 1, | ||
angle: 0, | ||
}, options); | ||
}; | ||
return Camera; | ||
}(events_1.EventEmitter)); | ||
exports.default = Camera; |
@@ -1,38 +0,34 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _events = require('events'); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js Captor Class | ||
* ====================== | ||
* | ||
* Abstract class representing a captor like the user's mouse or touch controls. | ||
*/ | ||
var Captor = function (_EventEmitter) { | ||
_inherits(Captor, _EventEmitter); | ||
function Captor(container, camera) { | ||
_classCallCheck(this, Captor); | ||
// Properties | ||
var _this = _possibleConstructorReturn(this, (Captor.__proto__ || Object.getPrototypeOf(Captor)).call(this)); | ||
_this.container = container; | ||
_this.camera = camera; | ||
return _this; | ||
} | ||
return Captor; | ||
}(_events.EventEmitter); | ||
exports.default = Captor; | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js Captor Class | ||
* ====================== | ||
* | ||
* Abstract class representing a captor like the user's mouse or touch controls. | ||
*/ | ||
var events_1 = require("events"); | ||
var Captor = /** @class */ (function (_super) { | ||
__extends(Captor, _super); | ||
function Captor(container, camera) { | ||
var _this = _super.call(this) || this; | ||
// Properties | ||
_this.container = container; | ||
_this.camera = camera; | ||
return _this; | ||
} | ||
return Captor; | ||
}(events_1.EventEmitter)); | ||
exports.default = Captor; |
@@ -1,41 +0,39 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _captor = require('../captor'); | ||
var _captor2 = _interopRequireDefault(_captor); | ||
var _utils = require('./utils'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js Mouse Captor | ||
* ====================== | ||
* | ||
* Sigma's captor dealing with the user's mouse. | ||
*/ | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js Mouse Captor | ||
* ====================== | ||
* | ||
* Sigma's captor dealing with the user's mouse. | ||
*/ | ||
var camera_1 = __importDefault(require("../camera")); | ||
var captor_1 = __importDefault(require("../captor")); | ||
var utils_1 = require("./utils"); | ||
/** | ||
* Constants. | ||
*/ | ||
var DRAG_TIMEOUT = 200, | ||
MOUSE_INERTIA_DURATION = 200, | ||
MOUSE_INERTIA_RATIO = 3, | ||
MOUSE_ZOOM_DURATION = 200, | ||
ZOOMING_RATIO = 1.7, | ||
DOUBLE_CLICK_TIMEOUT = 300, | ||
DOUBLE_CLICK_ZOOMING_RATIO = 2.2, | ||
DOUBLE_CLICK_ZOOMING_DURATION = 200; | ||
var DRAG_TIMEOUT = 200; | ||
var MOUSE_INERTIA_DURATION = 200; | ||
var MOUSE_INERTIA_RATIO = 3; | ||
var MOUSE_ZOOM_DURATION = 200; | ||
var ZOOMING_RATIO = 1.7; | ||
var DOUBLE_CLICK_TIMEOUT = 300; | ||
var DOUBLE_CLICK_ZOOMING_RATIO = 2.2; | ||
var DOUBLE_CLICK_ZOOMING_DURATION = 200; | ||
/** | ||
@@ -46,262 +44,242 @@ * Mouse captor class. | ||
*/ | ||
var MouseCaptor = function (_Captor) { | ||
_inherits(MouseCaptor, _Captor); | ||
function MouseCaptor(container, camera) { | ||
_classCallCheck(this, MouseCaptor); | ||
// Properties | ||
var _this = _possibleConstructorReturn(this, (MouseCaptor.__proto__ || Object.getPrototypeOf(MouseCaptor)).call(this, container, camera)); | ||
_this.container = container; | ||
_this.camera = camera; | ||
// State | ||
_this.enabled = true; | ||
_this.hasDragged = false; | ||
_this.downStartTime = null; | ||
_this.startMouseX = null; | ||
_this.startMouseY = null; | ||
_this.isMouseDown = false; | ||
_this.isMoving = true; | ||
_this.movingTimeout = null; | ||
_this.startCameraState = null; | ||
_this.lastCameraState = null; | ||
_this.clicks = 0; | ||
_this.doubleClickTimeout = null; | ||
_this.wheelLock = false; | ||
// Binding methods | ||
_this.handleClick = _this.handleClick.bind(_this); | ||
_this.handleDown = _this.handleDown.bind(_this); | ||
_this.handleUp = _this.handleUp.bind(_this); | ||
_this.handleMove = _this.handleMove.bind(_this); | ||
_this.handleWheel = _this.handleWheel.bind(_this); | ||
_this.handleOut = _this.handleOut.bind(_this); | ||
// Binding events | ||
container.addEventListener('click', _this.handleClick, false); | ||
container.addEventListener('mousedown', _this.handleDown, false); | ||
container.addEventListener('mousemove', _this.handleMove, false); | ||
container.addEventListener('DOMMouseScroll', _this.handleWheel, false); | ||
container.addEventListener('mousewheel', _this.handleWheel, false); | ||
container.addEventListener('mouseout', _this.handleOut, false); | ||
document.addEventListener('mouseup', _this.handleUp, false); | ||
return _this; | ||
} | ||
_createClass(MouseCaptor, [{ | ||
key: 'handleClick', | ||
value: function handleClick(e) { | ||
var _this2 = this; | ||
if (!this.enabled) return; | ||
this.clicks++; | ||
if (this.clicks === 2) { | ||
this.clicks = 0; | ||
clearTimeout(this.doubleClickTimeout); | ||
this.doubleClickTimeout = null; | ||
return this.handleDoubleClick(e); | ||
} | ||
setTimeout(function () { | ||
_this2.clicks = 0; | ||
_this2.doubleClickTimeout = null; | ||
}, DOUBLE_CLICK_TIMEOUT); | ||
this.emit('click', (0, _utils.getMouseCoords)(e, this.container.offsetLeft, this.container.offsetTop)); | ||
var MouseCaptor = /** @class */ (function (_super) { | ||
__extends(MouseCaptor, _super); | ||
function MouseCaptor(container, camera) { | ||
var _this = _super.call(this, container, camera) || this; | ||
// State | ||
_this.enabled = true; | ||
_this.hasDragged = false; | ||
_this.downStartTime = null; | ||
_this.lastMouseX = null; | ||
_this.lastMouseY = null; | ||
_this.isMouseDown = false; | ||
_this.isMoving = null; | ||
_this.movingTimeout = null; | ||
_this.startCameraState = null; | ||
_this.lastCameraState = null; | ||
_this.clicks = 0; | ||
_this.doubleClickTimeout = null; | ||
_this.wheelLock = false; | ||
// Binding methods | ||
_this.handleClick = _this.handleClick.bind(_this); | ||
_this.handleRightClick = _this.handleRightClick.bind(_this); | ||
_this.handleDown = _this.handleDown.bind(_this); | ||
_this.handleUp = _this.handleUp.bind(_this); | ||
_this.handleMove = _this.handleMove.bind(_this); | ||
_this.handleWheel = _this.handleWheel.bind(_this); | ||
_this.handleOut = _this.handleOut.bind(_this); | ||
// Binding events | ||
container.addEventListener("click", _this.handleClick, false); | ||
container.addEventListener("contextmenu", _this.handleRightClick, false); | ||
container.addEventListener("mousedown", _this.handleDown, false); | ||
container.addEventListener("mousemove", _this.handleMove, false); | ||
container.addEventListener("DOMMouseScroll", _this.handleWheel, false); | ||
container.addEventListener("mousewheel", _this.handleWheel, false); | ||
container.addEventListener("mouseout", _this.handleOut, false); | ||
document.addEventListener("mouseup", _this.handleUp, false); | ||
return _this; | ||
} | ||
}, { | ||
key: 'handleDoubleClick', | ||
value: function handleDoubleClick(e) { | ||
if (!this.enabled) return; | ||
var ratio = 1 / DOUBLE_CLICK_ZOOMING_RATIO; | ||
var center = (0, _utils.getCenter)(e); | ||
var cameraState = this.camera.getState(); | ||
var position = this.camera.abstractDisplayToGraph((0, _utils.getX)(e) - center.x, (0, _utils.getY)(e) - center.y); | ||
this.camera.animate({ | ||
x: position.x * (1 - ratio) + cameraState.x, | ||
y: position.y * (1 - ratio) + cameraState.y, | ||
ratio: ratio * cameraState.ratio | ||
}, { | ||
easing: 'quadraticInOut', | ||
duration: DOUBLE_CLICK_ZOOMING_DURATION | ||
}); | ||
if (e.preventDefault) e.preventDefault();else e.returnValue = false; | ||
e.stopPropagation(); | ||
return false; | ||
} | ||
}, { | ||
key: 'handleDown', | ||
value: function handleDown(e) { | ||
if (!this.enabled) return; | ||
this.startCameraState = this.camera.getState(); | ||
this.lastCameraState = this.startCameraState; | ||
this.startMouseX = (0, _utils.getX)(e); | ||
this.startMouseY = (0, _utils.getY)(e); | ||
this.hasDragged = false; | ||
this.downStartTime = Date.now(); | ||
// TODO: dispatch events | ||
switch (e.which) { | ||
default: | ||
// Left button pressed | ||
this.isMouseDown = true; | ||
this.emit('mousedown', (0, _utils.getMouseCoords)(e, this.container.offsetLeft, this.container.offsetTop)); | ||
} | ||
} | ||
}, { | ||
key: 'handleUp', | ||
value: function handleUp(e) { | ||
if (!this.enabled || !this.isMouseDown) return; | ||
this.isMouseDown = false; | ||
if (this.movingTimeout) { | ||
this.movingTimeout = null; | ||
clearTimeout(this.movingTimeout); | ||
} | ||
var x = (0, _utils.getX)(e), | ||
y = (0, _utils.getY)(e); | ||
var cameraState = this.camera.getState(); | ||
if (this.isMoving) { | ||
MouseCaptor.prototype.kill = function () { | ||
var container = this.container; | ||
container.removeEventListener("click", this.handleClick); | ||
container.removeEventListener("contextmenu", this.handleRightClick); | ||
container.removeEventListener("mousedown", this.handleDown); | ||
container.removeEventListener("mousemove", this.handleMove); | ||
container.removeEventListener("DOMMouseScroll", this.handleWheel); | ||
container.removeEventListener("mousewheel", this.handleWheel); | ||
container.removeEventListener("mouseout", this.handleOut); | ||
document.removeEventListener("mouseup", this.handleUp); | ||
}; | ||
MouseCaptor.prototype.handleClick = function (e) { | ||
var _this = this; | ||
if (!this.enabled) | ||
return; | ||
this.clicks++; | ||
if (this.clicks === 2) { | ||
this.clicks = 0; | ||
clearTimeout(this.doubleClickTimeout); | ||
this.doubleClickTimeout = null; | ||
return this.handleDoubleClick(e); | ||
} | ||
setTimeout(function () { | ||
_this.clicks = 0; | ||
_this.doubleClickTimeout = null; | ||
}, DOUBLE_CLICK_TIMEOUT); | ||
// NOTE: this is here to prevent click events on drag | ||
if (!this.hasDragged) | ||
this.emit("click", utils_1.getMouseCoords(e)); | ||
}; | ||
MouseCaptor.prototype.handleRightClick = function (e) { | ||
if (!this.enabled) | ||
return; | ||
this.emit("rightClick", utils_1.getMouseCoords(e)); | ||
}; | ||
MouseCaptor.prototype.handleDoubleClick = function (e) { | ||
if (!this.enabled) | ||
return; | ||
var center = utils_1.getCenter(e); | ||
var cameraState = this.camera.getState(); | ||
var newRatio = cameraState.ratio / DOUBLE_CLICK_ZOOMING_RATIO; | ||
// TODO: factorize | ||
var dimensions = { | ||
width: this.container.offsetWidth, | ||
height: this.container.offsetHeight, | ||
}; | ||
var clickX = utils_1.getX(e), clickY = utils_1.getY(e); | ||
// TODO: baaaad we mustn't mutate the camera, create a Camera.from or #.copy | ||
// TODO: factorize pan & zoomTo | ||
var cameraWithNewRatio = new camera_1.default(); | ||
cameraWithNewRatio.ratio = newRatio; | ||
cameraWithNewRatio.x = cameraState.x; | ||
cameraWithNewRatio.y = cameraState.y; | ||
var clickGraph = this.camera.viewportToGraph(dimensions, clickX, clickY), centerGraph = this.camera.viewportToGraph(dimensions, center.x, center.y); | ||
var clickGraphNew = cameraWithNewRatio.viewportToGraph(dimensions, clickX, clickY), centerGraphNew = cameraWithNewRatio.viewportToGraph(dimensions, center.x, center.y); | ||
var deltaX = clickGraphNew.x - centerGraphNew.x - clickGraph.x + centerGraph.x, deltaY = clickGraphNew.y - centerGraphNew.y - clickGraph.y + centerGraph.y; | ||
this.camera.animate({ | ||
x: cameraState.x + MOUSE_INERTIA_RATIO * (cameraState.x - this.lastCameraState.x), | ||
y: cameraState.y + MOUSE_INERTIA_RATIO * (cameraState.y - this.lastCameraState.y) | ||
x: cameraState.x - deltaX, | ||
y: cameraState.y - deltaY, | ||
ratio: newRatio, | ||
}, { | ||
duration: MOUSE_INERTIA_DURATION, | ||
easing: 'quadraticOut' | ||
easing: "quadraticInOut", | ||
duration: DOUBLE_CLICK_ZOOMING_DURATION, | ||
}); | ||
} else if (this.startMouseX !== x || this.startMouseY !== y) { | ||
this.camera.setState({ | ||
x: cameraState.x, | ||
y: cameraState.y | ||
}); | ||
} | ||
this.emit('mouseup', (0, _utils.getMouseCoords)(e, this.container.offsetLeft, this.container.offsetTop)); | ||
this.isMoving = false; | ||
} | ||
}, { | ||
key: 'handleMove', | ||
value: function handleMove(e) { | ||
var _this3 = this; | ||
if (!this.enabled) return; | ||
this.emit('mousemove', (0, _utils.getMouseCoords)(e, this.container.offsetLeft, this.container.offsetTop)); | ||
if (this.isMouseDown) { | ||
if (e.preventDefault) | ||
e.preventDefault(); | ||
else | ||
e.returnValue = false; | ||
e.stopPropagation(); | ||
return false; | ||
}; | ||
MouseCaptor.prototype.handleDown = function (e) { | ||
if (!this.enabled) | ||
return; | ||
this.startCameraState = this.camera.getState(); | ||
this.lastCameraState = this.startCameraState; | ||
this.lastMouseX = utils_1.getX(e); | ||
this.lastMouseY = utils_1.getY(e); | ||
this.hasDragged = false; | ||
this.downStartTime = Date.now(); | ||
// TODO: dispatch events | ||
this.isMoving = true; | ||
this.hasDragged = true; | ||
if (this.movingTimeout) clearTimeout(this.movingTimeout); | ||
this.movingTimeout = setTimeout(function () { | ||
_this3.movingTimeout = null; | ||
_this3.isMoving = false; | ||
}, DRAG_TIMEOUT); | ||
var position = this.camera.abstractDisplayToGraph((0, _utils.getX)(e) - this.startMouseX, (0, _utils.getY)(e) - this.startMouseY); | ||
var x = this.startCameraState.x - position.x, | ||
y = this.startCameraState.y - position.y; | ||
switch (e.which) { | ||
default: | ||
// Left button pressed | ||
this.isMouseDown = true; | ||
this.emit("mousedown", utils_1.getMouseCoords(e)); | ||
} | ||
}; | ||
MouseCaptor.prototype.handleUp = function (e) { | ||
var _this = this; | ||
if (!this.enabled || !this.isMouseDown) | ||
return; | ||
this.isMouseDown = false; | ||
if (this.movingTimeout) { | ||
this.movingTimeout = null; | ||
clearTimeout(this.movingTimeout); | ||
} | ||
var x = utils_1.getX(e), y = utils_1.getY(e); | ||
var cameraState = this.camera.getState(), previousCameraState = this.camera.getPreviousState(); | ||
if (this.isMoving) { | ||
this.camera.animate({ | ||
x: cameraState.x + MOUSE_INERTIA_RATIO * (cameraState.x - previousCameraState.x), | ||
y: cameraState.y + MOUSE_INERTIA_RATIO * (cameraState.y - previousCameraState.y), | ||
}, { | ||
duration: MOUSE_INERTIA_DURATION, | ||
easing: "quadraticOut", | ||
}); | ||
} | ||
else if (this.lastMouseX !== x || this.lastMouseY !== y) { | ||
this.camera.setState({ | ||
x: cameraState.x, | ||
y: cameraState.y, | ||
}); | ||
} | ||
this.isMoving = false; | ||
setImmediate(function () { return (_this.hasDragged = false); }); | ||
this.emit("mouseup", utils_1.getMouseCoords(e)); | ||
}; | ||
MouseCaptor.prototype.handleMove = function (e) { | ||
var _this = this; | ||
if (!this.enabled) | ||
return; | ||
this.emit("mousemove", utils_1.getMouseCoords(e)); | ||
if (this.isMouseDown) { | ||
// TODO: dispatch events | ||
this.isMoving = true; | ||
this.hasDragged = true; | ||
if (this.movingTimeout) | ||
clearTimeout(this.movingTimeout); | ||
this.movingTimeout = window.setTimeout(function () { | ||
_this.movingTimeout = null; | ||
_this.isMoving = false; | ||
}, DRAG_TIMEOUT); | ||
var dimensions = { | ||
width: this.container.offsetWidth, | ||
height: this.container.offsetHeight, | ||
}; | ||
var eX = utils_1.getX(e), eY = utils_1.getY(e); | ||
var lastMouse = this.camera.viewportToGraph(dimensions, this.lastMouseX, this.lastMouseY); | ||
var mouse = this.camera.viewportToGraph(dimensions, eX, eY); | ||
var offsetX = lastMouse.x - mouse.x, offsetY = lastMouse.y - mouse.y; | ||
var cameraState = this.camera.getState(); | ||
var x = cameraState.x + offsetX, y = cameraState.y + offsetY; | ||
this.camera.setState({ x: x, y: y }); | ||
this.lastMouseX = eX; | ||
this.lastMouseY = eY; | ||
} | ||
if (e.preventDefault) | ||
e.preventDefault(); | ||
else | ||
e.returnValue = false; | ||
e.stopPropagation(); | ||
return false; | ||
}; | ||
MouseCaptor.prototype.handleWheel = function (e) { | ||
var _this = this; | ||
if (e.preventDefault) | ||
e.preventDefault(); | ||
else | ||
e.returnValue = false; | ||
e.stopPropagation(); | ||
if (!this.enabled) | ||
return false; | ||
var delta = utils_1.getWheelDelta(e); | ||
if (!delta) | ||
return false; | ||
if (this.wheelLock) | ||
return false; | ||
this.wheelLock = true; | ||
// TODO: handle max zoom | ||
var ratio = delta > 0 ? 1 / ZOOMING_RATIO : ZOOMING_RATIO; | ||
var cameraState = this.camera.getState(); | ||
if (cameraState.x !== x || cameraState.y !== y) { | ||
this.lastCameraState = cameraState; | ||
this.camera.setState({ | ||
x: x, | ||
y: y | ||
}); | ||
} | ||
} | ||
if (e.preventDefault) e.preventDefault();else e.returnValue = false; | ||
e.stopPropagation(); | ||
return false; | ||
} | ||
}, { | ||
key: 'handleWheel', | ||
value: function handleWheel(e) { | ||
var _this4 = this; | ||
if (!this.enabled) return false; | ||
var delta = (0, _utils.getWheelDelta)(e); | ||
if (!delta) return false; | ||
if (this.wheelLock) return false; | ||
this.wheelLock = true; | ||
setTimeout(function () { | ||
return _this4.wheelLock = false; | ||
}, 30); | ||
// TODO: handle max zoom | ||
var ratio = delta > 0 ? 1 / ZOOMING_RATIO : ZOOMING_RATIO; | ||
var cameraState = this.camera.getState(); | ||
var newRatio = ratio * cameraState.ratio; | ||
var center = (0, _utils.getCenter)(e); | ||
var position = this.camera.abstractDisplayToGraph((0, _utils.getX)(e) - center.x, (0, _utils.getY)(e) - center.y); | ||
this.camera.animate({ | ||
x: position.x * (1 - ratio) + cameraState.x, | ||
y: position.y * (1 - ratio) + cameraState.y, | ||
ratio: newRatio | ||
}, { | ||
easing: this.camera.isAnimated() ? 'quadraticOut' : 'quadraticInOut', | ||
duration: MOUSE_ZOOM_DURATION | ||
}); | ||
if (e.preventDefault) e.preventDefault();else e.returnValue = false; | ||
e.stopPropagation(); | ||
return false; | ||
} | ||
}, { | ||
key: 'handleOut', | ||
value: function handleOut() { | ||
// TODO: dispatch event | ||
} | ||
}]); | ||
return MouseCaptor; | ||
}(_captor2.default); | ||
exports.default = MouseCaptor; | ||
var newRatio = ratio * cameraState.ratio; | ||
var center = utils_1.getCenter(e); | ||
var dimensions = { | ||
width: this.container.offsetWidth, | ||
height: this.container.offsetHeight, | ||
}; | ||
var clickX = utils_1.getX(e), clickY = utils_1.getY(e); | ||
// TODO: baaaad we mustn't mutate the camera, create a Camera.from or #.copy | ||
// TODO: factorize pan & zoomTo | ||
var cameraWithNewRatio = new camera_1.default(); | ||
cameraWithNewRatio.ratio = newRatio; | ||
cameraWithNewRatio.x = cameraState.x; | ||
cameraWithNewRatio.y = cameraState.y; | ||
var clickGraph = this.camera.viewportToGraph(dimensions, clickX, clickY), centerGraph = this.camera.viewportToGraph(dimensions, center.x, center.y); | ||
var clickGraphNew = cameraWithNewRatio.viewportToGraph(dimensions, clickX, clickY), centerGraphNew = cameraWithNewRatio.viewportToGraph(dimensions, center.x, center.y); | ||
var deltaX = clickGraphNew.x - centerGraphNew.x - clickGraph.x + centerGraph.x, deltaY = clickGraphNew.y - centerGraphNew.y - clickGraph.y + centerGraph.y; | ||
this.camera.animate({ | ||
x: cameraState.x - deltaX, | ||
y: cameraState.y - deltaY, | ||
ratio: newRatio, | ||
}, { | ||
easing: "linear", | ||
duration: MOUSE_ZOOM_DURATION, | ||
}, function () { return (_this.wheelLock = false); }); | ||
return false; | ||
}; | ||
MouseCaptor.prototype.handleOut = function () { | ||
// TODO: dispatch event | ||
}; | ||
return MouseCaptor; | ||
}(captor_1.default)); | ||
exports.default = MouseCaptor; |
@@ -1,26 +0,12 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** | ||
* Sigma.js Captor Utils | ||
* ====================== | ||
* | ||
* Miscellenous helper functions related to the captors. | ||
*/ | ||
exports.getX = getX; | ||
exports.getY = getY; | ||
exports.getWidth = getWidth; | ||
exports.getHeight = getHeight; | ||
exports.getCenter = getCenter; | ||
exports.getMouseCoords = getMouseCoords; | ||
exports.getWheelDelta = getWheelDelta; | ||
var _utils = require('../renderers/utils'); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getWheelDelta = exports.getMouseCoords = exports.getCenter = exports.getHeight = exports.getWidth = exports.getY = exports.getX = void 0; | ||
/** | ||
* Sigma.js Captor Utils | ||
* ====================== | ||
* | ||
* Miscellenous helper functions related to the captors. | ||
*/ | ||
var utils_1 = require("../renderers/utils"); | ||
/** | ||
* Extract the local X position from a mouse or touch event. | ||
@@ -32,11 +18,9 @@ * | ||
function getX(e) { | ||
if (typeof e.offsetX !== 'undefined') return e.offsetX; | ||
if (typeof e.layerX !== 'undefined') return e.layerX; | ||
if (typeof e.clientX !== 'undefined') return e.clientX; | ||
throw new Error('sigma/captors/utils.getX: could not extract x from event.'); | ||
if (typeof e.offsetX !== "undefined") | ||
return e.offsetX; | ||
if (typeof e.clientX !== "undefined") | ||
return e.clientX; | ||
throw new Error("sigma/captors/utils.getX: could not extract x from event."); | ||
} | ||
exports.getX = getX; | ||
/** | ||
@@ -49,11 +33,9 @@ * Extract the local Y position from a mouse or touch event. | ||
function getY(e) { | ||
if (typeof e.offsetY !== 'undefined') return e.offsetY; | ||
if (typeof e.layerY !== 'undefined') return e.layerY; | ||
if (typeof e.clientY !== 'undefined') return e.clientY; | ||
throw new Error('sigma/captors/utils.getY: could not extract y from event.'); | ||
if (typeof e.offsetY !== "undefined") | ||
return e.offsetY; | ||
if (typeof e.clientY !== "undefined") | ||
return e.clientY; | ||
throw new Error("sigma/captors/utils.getY: could not extract y from event."); | ||
} | ||
exports.getY = getY; | ||
/** | ||
@@ -66,11 +48,8 @@ * Extract the width from a mouse or touch event. | ||
function getWidth(e) { | ||
var w = !e.target.ownerSVGElement ? e.target.width : e.target.ownerSVGElement.width; | ||
if (typeof w === 'number') return w; | ||
if (w !== undefined && w.baseVal !== undefined) return w.baseVal.value; | ||
throw new Error('sigma/captors/utils.getWidth: could not extract width from event.'); | ||
var w = e.target.width; | ||
if (typeof w === "number") | ||
return w; | ||
throw new Error("sigma/captors/utils.getWidth: could not extract width from event."); | ||
} | ||
exports.getWidth = getWidth; | ||
/** | ||
@@ -83,11 +62,8 @@ * Extract the height from a mouse or touch event. | ||
function getHeight(e) { | ||
var w = !e.target.ownerSVGElement ? e.target.height : e.target.ownerSVGElement.height; | ||
if (typeof w === 'number') return w; | ||
if (w !== undefined && w.baseVal !== undefined) return w.baseVal.value; | ||
throw new Error('sigma/captors/utils.getHeight: could not extract height from event.'); | ||
var w = e.target.height; | ||
if (typeof w === "number") | ||
return w; | ||
throw new Error("sigma/captors/utils.getHeight: could not extract height from event."); | ||
} | ||
exports.getHeight = getHeight; | ||
/** | ||
@@ -100,10 +76,9 @@ * Extract the center from a mouse or touch event. | ||
function getCenter(e) { | ||
var ratio = e.target.namespaceURI.indexOf('svg') !== -1 ? 1 : (0, _utils.getPixelRatio)(); | ||
return { | ||
x: getWidth(e) / (2 * ratio), | ||
y: getHeight(e) / (2 * ratio) | ||
}; | ||
var ratio = utils_1.getPixelRatio(); | ||
return { | ||
x: getWidth(e) / (2 * ratio), | ||
y: getHeight(e) / (2 * ratio), | ||
}; | ||
} | ||
exports.getCenter = getCenter; | ||
/** | ||
@@ -118,21 +93,18 @@ * Convert mouse coords to sigma coords. | ||
*/ | ||
function getMouseCoords(e, x, y) { | ||
if (arguments.length < 2) { | ||
x = getX(e); | ||
y = getY(e); | ||
} | ||
// const center = getCenter(e); | ||
return { | ||
x: e.clientX - x, | ||
y: e.clientY - y, | ||
clientX: e.clientX, | ||
clientY: e.clientY, | ||
ctrlKey: e.ctrlKey, | ||
metaKey: e.metaKey, | ||
altKey: e.altKey, | ||
shiftKey: e.shiftKey | ||
}; | ||
function getMouseCoords(e) { | ||
return { | ||
x: getX(e), | ||
y: getY(e), | ||
clientX: e.clientX, | ||
clientY: e.clientY, | ||
ctrlKey: e.ctrlKey, | ||
metaKey: e.metaKey, | ||
altKey: e.altKey, | ||
shiftKey: e.shiftKey, | ||
// TODO: this is not ideal... But I am wondering why we don't just pass the event through | ||
preventDefault: e.preventDefault.bind(e), | ||
original: e, | ||
}; | ||
} | ||
exports.getMouseCoords = getMouseCoords; | ||
/** | ||
@@ -145,7 +117,9 @@ * Extract the wheel delta from a mouse or touch event. | ||
function getWheelDelta(e) { | ||
if (_typeof(e.wheelDelta) !== undefined) return e.wheelDelta; | ||
if (_typeof(e.detail) !== undefined) return -e.detail; | ||
throw new Error('sigma/captors/utils.getDelta: could not extract delta from event.'); | ||
} | ||
// TODO: check those ratios again to ensure a clean Chrome/Firefox compat | ||
if (typeof e.deltaY !== "undefined") | ||
return (e.deltaY * -3) / 360; | ||
if (typeof e.detail !== "undefined") | ||
return e.detail / -9; | ||
throw new Error("sigma/captors/utils.getDelta: could not extract delta from event."); | ||
} | ||
exports.getWheelDelta = getWheelDelta; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.cubicInOut = exports.cubicOut = exports.cubicIn = exports.quadraticInOut = exports.quadraticOut = exports.quadraticIn = exports.linear = void 0; | ||
/** | ||
@@ -12,30 +10,16 @@ * Sigma.js Easings | ||
*/ | ||
var linear = exports.linear = function linear(k) { | ||
return k; | ||
exports.linear = function (k) { return k; }; | ||
exports.quadraticIn = function (k) { return k * k; }; | ||
exports.quadraticOut = function (k) { return k * (2 - k); }; | ||
exports.quadraticInOut = function (k) { | ||
if ((k *= 2) < 1) | ||
return 0.5 * k * k; | ||
return -0.5 * (--k * (k - 2) - 1); | ||
}; | ||
var quadraticIn = exports.quadraticIn = function quadraticIn(k) { | ||
return k * k; | ||
exports.cubicIn = function (k) { return k * k * k; }; | ||
exports.cubicOut = function (k) { return --k * k * k + 1; }; | ||
exports.cubicInOut = function (k) { | ||
if ((k *= 2) < 1) | ||
return 0.5 * k * k * k; | ||
return 0.5 * ((k -= 2) * k * k + 2); | ||
}; | ||
var quadraticOut = exports.quadraticOut = function quadraticOut(k) { | ||
return k * (2 - k); | ||
}; | ||
var quadraticInOut = exports.quadraticInOut = function quadraticInOut(k) { | ||
if ((k *= 2) < 1) return 0.5 * k * k; | ||
return -0.5 * (--k * (k - 2) - 1); | ||
}; | ||
var cubicIn = exports.cubicIn = function cubicIn(k) { | ||
return k * k * k; | ||
}; | ||
var cubicOut = exports.cubicOut = function cubicOut(k) { | ||
return --k * k * k + 1; | ||
}; | ||
var cubicInOut = exports.cubicInOut = function cubicInOut(k) { | ||
if ((k *= 2) < 1) return 0.5 * k * k * k; | ||
return 0.5 * ((k -= 2) * k * k + 2); | ||
}; |
@@ -1,29 +0,7 @@ | ||
'use strict'; | ||
var _sigma = require('./sigma'); | ||
var _sigma2 = _interopRequireDefault(_sigma); | ||
var _renderer = require('./renderer'); | ||
var _renderer2 = _interopRequireDefault(_renderer); | ||
var _camera = require('./camera'); | ||
var _camera2 = _interopRequireDefault(_camera); | ||
var _quadtree = require('./quadtree'); | ||
var _quadtree2 = _interopRequireDefault(_quadtree); | ||
var _mouse = require('./captors/mouse'); | ||
var _mouse2 = _interopRequireDefault(_mouse); | ||
var _webgl = require('./renderers/webgl'); | ||
var _webgl2 = _interopRequireDefault(_webgl); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WebGLRenderer = exports.MouseCaptor = exports.QuadTree = exports.Camera = exports.Renderer = void 0; | ||
/** | ||
@@ -35,13 +13,11 @@ * Sigma.js Library Endpoint | ||
*/ | ||
var library = { | ||
Sigma: _sigma2.default, | ||
Renderer: _renderer2.default, | ||
Camera: _camera2.default, | ||
QuadTree: _quadtree2.default, | ||
MouseCaptor: _mouse2.default, | ||
WebGLRenderer: _webgl2.default | ||
}; | ||
for (var k in library) { | ||
_sigma2.default[k] = library[k]; | ||
}module.exports = _sigma2.default; | ||
var renderer_1 = __importDefault(require("./renderer")); | ||
exports.Renderer = renderer_1.default; | ||
var camera_1 = __importDefault(require("./camera")); | ||
exports.Camera = camera_1.default; | ||
var quadtree_1 = __importDefault(require("./quadtree")); | ||
exports.QuadTree = quadtree_1.default; | ||
var mouse_1 = __importDefault(require("./captors/mouse")); | ||
exports.MouseCaptor = mouse_1.default; | ||
var webgl_1 = __importDefault(require("./renderers/webgl")); | ||
exports.WebGLRenderer = webgl_1.default; |
@@ -1,2 +0,2 @@ | ||
Copyright (C) 2013-2017, Alexis Jacomy, Guillaume Plique http://sigmajs.org | ||
Copyright (C) 2013-2020, Alexis Jacomy, Guillaume Plique http://sigmajs.org | ||
@@ -3,0 +3,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), |
100
package.json
{ | ||
"name": "sigma", | ||
"version": "2.0.0-alpha4", | ||
"version": "2.0.0-alpha40", | ||
"description": "A JavaScript library dedicated to graph drawing.", | ||
"homepage": "http://sigmajs.org", | ||
"bugs": "http://github.com/jacomyal/sigma.js/issues", | ||
"main": "sigma.js", | ||
"main": "endpoint.js", | ||
"typings": "types/endpoint.d.ts", | ||
"scripts": { | ||
"build": "webpack --colors --progress && webpack --colors --progress -p", | ||
"clean": "rimraf $npm_package_sigma_clean", | ||
"dist": "npm run clean && babel src --out-dir . && webpack --config ./webpack.programs.config.js", | ||
"transpile": "tsc -p ./tsconfig.dist.json -d --declarationDir types && replace '\\.glsl' '.glsl.js' renderers -r && webpack --config ./webpack.programs.config.js", | ||
"examples": "webpack-dev-server --config ./examples/webpack.config.js --progress", | ||
"lint": "eslint examples src test", | ||
"gitignore": "jq -r '.sigma.clean' package.json | tr ' ' '\\n' >> .gitignore", | ||
"lint": "eslint $npm_package_sigma_folders", | ||
"postpublish": "npm run clean", | ||
"prepublish": "npm run test && npm run dist && npm run build", | ||
"test": "mocha --compilers js:babel-core/register ./test/endpoint.js" | ||
"prettier": "prettier --write $npm_package_sigma_folders", | ||
"prepublish": "echo \"Linter should go here...\" && npm run test && npm run clean && npm run transpile && npm run build", | ||
"test": "mocha --require ts-node/register --recursive test --extension ts", | ||
"types": "npx tsc --declaration --emitDeclarationOnly --outDir lib" | ||
}, | ||
"sigma": { | ||
"clean": "camera.js captor.js easings.js endpoint.js quadtree.js renderer.js sigma.js utils.js captors renderers" | ||
"clean": "animate.js camera.js captor.js easings.js endpoint.js quadtree.js renderer.js sigma-graphology.js utils.js captors heuristics renderers types", | ||
"folders": "examples src test" | ||
}, | ||
@@ -36,23 +41,41 @@ "repository": { | ||
"license": "MIT", | ||
"dependencies": { | ||
"@yomguithereal/helpers": "^1.0.0", | ||
"events": "^3.0.0", | ||
"graphology-metrics": "1.13.0", | ||
"graphology-utils": "^1.4.0" | ||
}, | ||
"devDependencies": { | ||
"@yomguithereal/eslint-config": "^4.0.0", | ||
"babel-cli": "^6.24.1", | ||
"babel-core": "^6.14.0", | ||
"babel-loader": "^7.0.0", | ||
"babel-preset-es2015": "^6.14.0", | ||
"chroma-js": "^1.3.3", | ||
"eslint": "^4.0.0", | ||
"@babel/core": "^7.9.6", | ||
"@types/mocha": "^7.0.2", | ||
"@typescript-eslint/eslint-plugin": "^3.3.0", | ||
"@typescript-eslint/parser": "^3.3.0", | ||
"babel-eslint": "^10.0.3", | ||
"babel-loader": "^8.0.6", | ||
"chroma-js": "^2.1.0", | ||
"d3-scale": "^3.2.1", | ||
"eslint": "^7.3.0", | ||
"faker": "^4.1.0", | ||
"graphology": "^0.10.2", | ||
"graphology-generators": "^0.6.1", | ||
"graphology-gexf": "^0.3.1", | ||
"graphology-layout": "0.0.1", | ||
"graphology-layout-forceatlas2": "0.1.0", | ||
"html-webpack-plugin": "^2.22.0", | ||
"mocha": "^3.0.2", | ||
"raw-loader": "^0.5.1", | ||
"rimraf": "^2.6.1", | ||
"webpack": "^2.5.1", | ||
"webpack-dev-server": "^2.4.5", | ||
"worker-loader": "^0.8.0" | ||
"glob": "^7.1.3", | ||
"graphology": "^0.17.1", | ||
"graphology-communities-louvain": "^1.2.0", | ||
"graphology-components": "^0.1.0", | ||
"graphology-generators": "^0.10.1", | ||
"graphology-gexf": "^0.5.0", | ||
"graphology-layout": "0.3.1", | ||
"graphology-layout-forceatlas2": "^0.4.4", | ||
"html-webpack-plugin": "^3.2.0", | ||
"mocha": "^8.0.1", | ||
"pandemonium": "^1.4.1", | ||
"prettier": "^2.0.5", | ||
"raw-loader": "^4.0.1", | ||
"replace": "^1.2.0", | ||
"rimraf": "^3.0.0", | ||
"simple-statistics": "^7.0.7", | ||
"ts-loader": "^7.0.2", | ||
"ts-node": "^8.10.1", | ||
"typescript": "^3.8.3", | ||
"webpack": "^4.20.2", | ||
"webpack-cli": "^3.1.2", | ||
"webpack-dev-server": "^3.1.9" | ||
}, | ||
@@ -63,19 +86,18 @@ "keywords": [ | ||
"renderer", | ||
"sigma", | ||
"canvas", | ||
"webgl", | ||
"svg" | ||
"webgl" | ||
], | ||
"babel": { | ||
"presets": [ | ||
"es2015" | ||
] | ||
}, | ||
"eslintConfig": { | ||
"extends": "@yomguithereal/eslint-config/es6" | ||
}, | ||
"dependencies": { | ||
"events": "^1.1.1", | ||
"gl-matrix": "^2.3.2", | ||
"graphology-utils": "^1.1.1" | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": [ | ||
"@typescript-eslint" | ||
], | ||
"extends": [ | ||
"plugin:@typescript-eslint/recommended" | ||
], | ||
"rules": { | ||
"@typescript-eslint/camelcase": "off" | ||
} | ||
} | ||
} |
445
quadtree.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* eslint no-nested-ternary: 0 */ | ||
/* eslint no-constant-condition: 0 */ | ||
/** | ||
@@ -20,14 +16,13 @@ * Sigma.js Quad Tree Class | ||
*/ | ||
var extend_1 = __importDefault(require("@yomguithereal/helpers/extend")); | ||
// TODO: should not ask the quadtree when the camera has the whole graph in | ||
// sight. | ||
// TODO: a square can be represented as topleft + width | ||
// 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 max | ||
// TODO: decide whether to store at leaf level or at medium levels (frustum vs. hover) | ||
// 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 | ||
/** | ||
@@ -43,19 +38,8 @@ * Constants. | ||
*/ | ||
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; | ||
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. | ||
*/ | ||
/** | ||
@@ -71,18 +55,14 @@ * Function returning whether the given rectangle is axis-aligned. | ||
function isAxisAligned(x1, y1, x2, y2) { | ||
return x1 === x2 || y1 === y2; | ||
return x1 === x2 || y1 === y2; | ||
} | ||
function rectangleCollidesWithQuad(x1, y1, w, qx, qy, qw, qh) { | ||
return x1 < qx + qw && x1 + w > qx && y1 < qy + qh && y1 + w > qy; | ||
function squareCollidesWithQuad(x1, y1, w, qx, qy, qw, qh) { | ||
return x1 < qx + qw && x1 + w > qx && y1 < qy + qh && y1 + w > qy; | ||
} | ||
function rectangleCollidesWithQuad(x1, y1, w, h, qx, qy, qw, qh) { | ||
return x1 < qx + qw && x1 + w > qx && y1 < qy + qh && y1 + h > qy; | ||
} | ||
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; | ||
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; | ||
} | ||
/** | ||
@@ -93,114 +73,141 @@ * Helper functions that are not bound to the class so an external user | ||
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 * BLOCKS, | ||
bottomLeftBlock = 4 * block + 3 * BLOCKS, | ||
bottomRightBlock = 4 * block + 4 * BLOCKS; | ||
var x = data[block + X_OFFSET], | ||
y = data[block + Y_OFFSET], | ||
width = data[block + WIDTH_OFFSET], | ||
height = data[block + HEIGHT_OFFSET], | ||
hw = width / 2, | ||
hh = height / 2; | ||
data[topLeftBlock + X_OFFSET] = x; | ||
data[topLeftBlock + Y_OFFSET] = y; | ||
data[topLeftBlock + WIDTH_OFFSET] = hw; | ||
data[topLeftBlock + HEIGHT_OFFSET] = hh; | ||
data[topRightBlock + X_OFFSET] = x + hw; | ||
data[topRightBlock + Y_OFFSET] = y; | ||
data[topRightBlock + WIDTH_OFFSET] = hw; | ||
data[topRightBlock + HEIGHT_OFFSET] = hh; | ||
data[bottomLeftBlock + X_OFFSET] = x; | ||
data[bottomLeftBlock + Y_OFFSET] = y + hh; | ||
data[bottomLeftBlock + WIDTH_OFFSET] = hw; | ||
data[bottomLeftBlock + HEIGHT_OFFSET] = hh; | ||
data[bottomRightBlock + X_OFFSET] = x + hw; | ||
data[bottomRightBlock + Y_OFFSET] = y + hh; | ||
data[bottomRightBlock + WIDTH_OFFSET] = hw; | ||
data[bottomRightBlock + HEIGHT_OFFSET] = hh; | ||
if (level < maxLevel - 1) { | ||
stack.push(bottomRightBlock, level + 1); | ||
stack.push(bottomLeftBlock, level + 1); | ||
stack.push(topRightBlock, level + 1); | ||
stack.push(topLeftBlock, level + 1); | ||
// [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 * BLOCKS, bottomLeftBlock = 4 * block + 3 * BLOCKS, bottomRightBlock = 4 * block + 4 * BLOCKS; | ||
var x = data[block + X_OFFSET], y = data[block + Y_OFFSET], width = data[block + WIDTH_OFFSET], height = data[block + HEIGHT_OFFSET], hw = width / 2, hh = height / 2; | ||
data[topLeftBlock + X_OFFSET] = x; | ||
data[topLeftBlock + Y_OFFSET] = y; | ||
data[topLeftBlock + WIDTH_OFFSET] = hw; | ||
data[topLeftBlock + HEIGHT_OFFSET] = hh; | ||
data[topRightBlock + X_OFFSET] = x + hw; | ||
data[topRightBlock + Y_OFFSET] = y; | ||
data[topRightBlock + WIDTH_OFFSET] = hw; | ||
data[topRightBlock + HEIGHT_OFFSET] = hh; | ||
data[bottomLeftBlock + X_OFFSET] = x; | ||
data[bottomLeftBlock + Y_OFFSET] = y + hh; | ||
data[bottomLeftBlock + WIDTH_OFFSET] = hw; | ||
data[bottomLeftBlock + HEIGHT_OFFSET] = hh; | ||
data[bottomRightBlock + X_OFFSET] = x + hw; | ||
data[bottomRightBlock + Y_OFFSET] = y + hh; | ||
data[bottomRightBlock + WIDTH_OFFSET] = hw; | ||
data[bottomRightBlock + HEIGHT_OFFSET] = hh; | ||
if (level < maxLevel - 1) { | ||
stack.push(bottomRightBlock, level + 1); | ||
stack.push(bottomLeftBlock, level + 1); | ||
stack.push(topRightBlock, level + 1); | ||
stack.push(topLeftBlock, level + 1); | ||
} | ||
} | ||
} | ||
} | ||
function insertNode(maxLevel, data, containers, key, x, y, size) { | ||
var x1 = x - size, | ||
y1 = y - size, | ||
w = size * 2; | ||
// [block, level] | ||
// TODO: does not require a stack if sticking with mid-level containers | ||
var stack = [0, 0]; | ||
while (stack.length) { | ||
var level = stack.pop(); | ||
var block = stack.pop(); | ||
// If we reached max level | ||
if (level >= maxLevel) { | ||
containers[block] = containers[block] || []; | ||
containers[block].push(key); | ||
return; | ||
var x1 = x - size, y1 = y - size, w = size * 2; | ||
var level = 0, block = 0; | ||
while (true) { | ||
// If we reached max level | ||
if (level >= maxLevel) { | ||
containers[block] = containers[block] || []; | ||
containers[block].push(key); | ||
return; | ||
} | ||
var topLeftBlock = 4 * block + BLOCKS, topRightBlock = 4 * block + 2 * BLOCKS, bottomLeftBlock = 4 * block + 3 * BLOCKS, bottomRightBlock = 4 * block + 4 * BLOCKS; | ||
var collidingWithTopLeft = squareCollidesWithQuad(x1, y1, w, data[topLeftBlock + X_OFFSET], data[topLeftBlock + Y_OFFSET], data[topLeftBlock + WIDTH_OFFSET], data[topLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithTopRight = squareCollidesWithQuad(x1, y1, w, data[topRightBlock + X_OFFSET], data[topRightBlock + Y_OFFSET], data[topRightBlock + WIDTH_OFFSET], data[topRightBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomLeft = squareCollidesWithQuad(x1, y1, w, data[bottomLeftBlock + X_OFFSET], data[bottomLeftBlock + Y_OFFSET], data[bottomLeftBlock + WIDTH_OFFSET], data[bottomLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomRight = squareCollidesWithQuad(x1, y1, w, data[bottomRightBlock + X_OFFSET], data[bottomRightBlock + Y_OFFSET], data[bottomRightBlock + WIDTH_OFFSET], data[bottomRightBlock + HEIGHT_OFFSET]); | ||
var collisions = [ | ||
collidingWithTopLeft, | ||
collidingWithTopRight, | ||
collidingWithBottomLeft, | ||
collidingWithBottomRight, | ||
].reduce(function (acc, current) { | ||
if (current) | ||
return acc + 1; | ||
else | ||
return acc; | ||
}, 0); | ||
// If we don't have at least a collision, there is an issue | ||
if (collisions === 0) | ||
throw new Error("sigma/quadtree.insertNode: no collision (level: " + level + ", key: " + key + ", x: " + x + ", y: " + y + ", size: " + size + ")."); | ||
// If we have 3 collisions, we have a geometry problem obviously | ||
if (collisions === 3) | ||
throw new Error("sigma/quadtree.insertNode: 3 impossible collisions (level: " + level + ", key: " + key + ", x: " + x + ", y: " + y + ", size: " + size + ")."); | ||
// If we have more that one collision, we stop here and store the node | ||
// in the relevant containers | ||
if (collisions > 1) { | ||
// NOTE: this is a nice way to optimize for hover, but not for frustum | ||
// since it requires to uniq the collected nodes | ||
// if (collisions < 4) { | ||
// // If we intersect two quads, we place the node in those two | ||
// if (collidingWithTopLeft) { | ||
// containers[topLeftBlock] = containers[topLeftBlock] || []; | ||
// containers[topLeftBlock].push(key); | ||
// } | ||
// if (collidingWithTopRight) { | ||
// containers[topRightBlock] = containers[topRightBlock] || []; | ||
// containers[topRightBlock].push(key); | ||
// } | ||
// if (collidingWithBottomLeft) { | ||
// containers[bottomLeftBlock] = containers[bottomLeftBlock] || []; | ||
// containers[bottomLeftBlock].push(key); | ||
// } | ||
// if (collidingWithBottomRight) { | ||
// containers[bottomRightBlock] = containers[bottomRightBlock] || []; | ||
// containers[bottomRightBlock].push(key); | ||
// } | ||
// } | ||
// else { | ||
// // Else we keep the node where it is to avoid more pointless computations | ||
// containers[block] = containers[block] || []; | ||
// containers[block].push(key); | ||
// } | ||
containers[block] = containers[block] || []; | ||
containers[block].push(key); | ||
return; | ||
} | ||
else { | ||
level++; | ||
} | ||
// Else we recurse into the correct quads | ||
if (collidingWithTopLeft) | ||
block = topLeftBlock; | ||
if (collidingWithTopRight) | ||
block = topRightBlock; | ||
if (collidingWithBottomLeft) | ||
block = bottomLeftBlock; | ||
if (collidingWithBottomRight) | ||
block = bottomRightBlock; | ||
} | ||
var topLeftBlock = 4 * block + BLOCKS, | ||
topRightBlock = 4 * block + 2 * BLOCKS, | ||
bottomLeftBlock = 4 * block + 3 * BLOCKS, | ||
bottomRightBlock = 4 * block + 4 * BLOCKS; | ||
var collidingWithTopLeft = rectangleCollidesWithQuad(x1, y1, w, data[topLeftBlock + X_OFFSET], data[topLeftBlock + Y_OFFSET], data[topLeftBlock + WIDTH_OFFSET], data[topLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithTopRight = rectangleCollidesWithQuad(x1, y1, w, data[topRightBlock + X_OFFSET], data[topRightBlock + Y_OFFSET], data[topRightBlock + WIDTH_OFFSET], data[topRightBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomLeft = rectangleCollidesWithQuad(x1, y1, w, data[bottomLeftBlock + X_OFFSET], data[bottomLeftBlock + Y_OFFSET], data[bottomLeftBlock + WIDTH_OFFSET], data[bottomLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomRight = rectangleCollidesWithQuad(x1, y1, w, data[bottomRightBlock + X_OFFSET], data[bottomRightBlock + Y_OFFSET], data[bottomRightBlock + WIDTH_OFFSET], data[bottomRightBlock + HEIGHT_OFFSET]); | ||
var collisions = collidingWithTopLeft + collidingWithTopRight + collidingWithBottomLeft + collidingWithBottomRight; | ||
// If we don't have at least a collision, there is an issue | ||
if (collisions === 0) throw new Error("sigma/quadtree.insertNode: no collision (level: " + level + ", key: " + key + ", x: " + x + ", y: " + y + ", size: " + size + ")."); | ||
// If we have 3 collisions, we have a geometry problem obviously | ||
if (collisions === 3) throw new Error("sigma/quadtree.insertNode: 3 impossible collisions (level: " + level + ", key: " + key + ", x: " + x + ", y: " + y + ", size: " + size + ")."); | ||
// If we have more that one collision, we stop here and store the node | ||
// in the relevant containers | ||
if (collisions > 1) { | ||
containers[block] = containers[block] || []; | ||
containers[block].push(key); | ||
return; | ||
} else { | ||
level++; | ||
} | ||
function getNodesInAxisAlignedRectangleArea(maxLevel, data, containers, x1, y1, w, h) { | ||
// [block, level] | ||
var stack = [0, 0]; | ||
var collectedNodes = []; | ||
var container; | ||
while (stack.length) { | ||
var level = stack.pop(), block = stack.pop(); | ||
// Collecting nodes | ||
container = containers[block]; | ||
if (container) | ||
extend_1.default(collectedNodes, container); | ||
// If we reached max level | ||
if (level >= maxLevel) | ||
continue; | ||
var topLeftBlock = 4 * block + BLOCKS, topRightBlock = 4 * block + 2 * BLOCKS, bottomLeftBlock = 4 * block + 3 * BLOCKS, bottomRightBlock = 4 * block + 4 * BLOCKS; | ||
var collidingWithTopLeft = rectangleCollidesWithQuad(x1, y1, w, h, data[topLeftBlock + X_OFFSET], data[topLeftBlock + Y_OFFSET], data[topLeftBlock + WIDTH_OFFSET], data[topLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithTopRight = rectangleCollidesWithQuad(x1, y1, w, h, data[topRightBlock + X_OFFSET], data[topRightBlock + Y_OFFSET], data[topRightBlock + WIDTH_OFFSET], data[topRightBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomLeft = rectangleCollidesWithQuad(x1, y1, w, h, data[bottomLeftBlock + X_OFFSET], data[bottomLeftBlock + Y_OFFSET], data[bottomLeftBlock + WIDTH_OFFSET], data[bottomLeftBlock + HEIGHT_OFFSET]); | ||
var collidingWithBottomRight = rectangleCollidesWithQuad(x1, y1, w, h, data[bottomRightBlock + X_OFFSET], data[bottomRightBlock + Y_OFFSET], data[bottomRightBlock + WIDTH_OFFSET], data[bottomRightBlock + HEIGHT_OFFSET]); | ||
if (collidingWithTopLeft) | ||
stack.push(topLeftBlock, level + 1); | ||
if (collidingWithTopRight) | ||
stack.push(topRightBlock, level + 1); | ||
if (collidingWithBottomLeft) | ||
stack.push(bottomLeftBlock, level + 1); | ||
if (collidingWithBottomRight) | ||
stack.push(bottomRightBlock, level + 1); | ||
} | ||
// Else we recurse into the correct quads | ||
if (collidingWithTopLeft) stack.push(topLeftBlock, level); | ||
if (collidingWithTopRight) stack.push(topRightBlock, level); | ||
if (collidingWithBottomLeft) stack.push(bottomLeftBlock, level); | ||
if (collidingWithBottomRight) stack.push(bottomRightBlock, level); | ||
} | ||
return collectedNodes; | ||
} | ||
/** | ||
@@ -210,69 +217,75 @@ * QuadTree class. | ||
* @constructor | ||
* @param {Graph} graph - A graph instance. | ||
* @param {object} boundaries - The graph boundaries. | ||
*/ | ||
var QuadTree = function () { | ||
function QuadTree(boundaries) { | ||
_classCallCheck(this, QuadTree); | ||
// Allocating the underlying byte array | ||
var L = Math.pow(4, MAX_LEVEL); | ||
this.data = new Float32Array(BLOCKS * ((4 * L - 1) / 3)); | ||
this.containers = {}; | ||
if (boundaries) this.resize(boundaries); | ||
} | ||
_createClass(QuadTree, [{ | ||
key: "add", | ||
value: function add(key, x, y, size) { | ||
insertNode(MAX_LEVEL, this.data, this.containers, key, x, y, size); | ||
return this; | ||
var QuadTree = /** @class */ (function () { | ||
function QuadTree(params) { | ||
if (params === void 0) { params = {}; } | ||
this.containers = {}; | ||
this.cache = null; | ||
this.lastRectangle = null; | ||
// Allocating the underlying byte array | ||
var L = Math.pow(4, MAX_LEVEL); | ||
this.data = new Float32Array(BLOCKS * ((4 * L - 1) / 3)); | ||
if (params.boundaries) | ||
this.resize(params.boundaries); | ||
else | ||
this.resize({ | ||
x: 0, | ||
y: 0, | ||
width: 1, | ||
height: 1, | ||
}); | ||
if (typeof params.filter === "function") | ||
this.nodeFilter = params.filter; | ||
} | ||
}, { | ||
key: "resize", | ||
value: function resize(boundaries) { | ||
this.clear(); | ||
// Building the quadrants | ||
this.data[X_OFFSET] = boundaries.x; | ||
this.data[Y_OFFSET] = boundaries.y; | ||
this.data[WIDTH_OFFSET] = boundaries.width; | ||
this.data[HEIGHT_OFFSET] = boundaries.height; | ||
buildQuadrants(MAX_LEVEL, this.data); | ||
} | ||
}, { | ||
key: "clear", | ||
value: function clear() { | ||
this.containers = {}; | ||
return this; | ||
} | ||
}, { | ||
key: "point", | ||
value: function point(x, y) { | ||
var nodes = []; | ||
var block = 0, | ||
level = 0; | ||
do { | ||
if (this.containers[block]) nodes.push.apply(nodes, this.containers[block]); | ||
var quad = pointIsInQuad(x, y, this.data[block + X_OFFSET], this.data[block + Y_OFFSET], this.data[block + WIDTH_OFFSET], this.data[block + HEIGHT_OFFSET]); | ||
block = 4 * block + quad * BLOCKS; | ||
level++; | ||
} while (level <= MAX_LEVEL); | ||
return nodes; | ||
} | ||
}]); | ||
return QuadTree; | ||
}(); | ||
exports.default = QuadTree; | ||
QuadTree.prototype.add = function (key, x, y, size) { | ||
insertNode(MAX_LEVEL, this.data, this.containers, key, x, y, size); | ||
return this; | ||
}; | ||
QuadTree.prototype.resize = function (boundaries) { | ||
this.clear(); | ||
// Building the quadrants | ||
this.data[X_OFFSET] = boundaries.x; | ||
this.data[Y_OFFSET] = boundaries.y; | ||
this.data[WIDTH_OFFSET] = boundaries.width; | ||
this.data[HEIGHT_OFFSET] = boundaries.height; | ||
buildQuadrants(MAX_LEVEL, this.data); | ||
}; | ||
QuadTree.prototype.clear = function () { | ||
this.containers = {}; | ||
return this; | ||
}; | ||
QuadTree.prototype.point = function (x, y) { | ||
var nodes = []; | ||
var block = 0, level = 0; | ||
do { | ||
if (this.containers[block]) | ||
nodes.push.apply(nodes, this.containers[block]); | ||
var quad = pointIsInQuad(x, y, this.data[block + X_OFFSET], this.data[block + Y_OFFSET], this.data[block + WIDTH_OFFSET], this.data[block + HEIGHT_OFFSET]); | ||
block = 4 * block + quad * BLOCKS; | ||
level++; | ||
} while (level <= MAX_LEVEL); | ||
return nodes; | ||
}; | ||
QuadTree.prototype.rectangle = function (x1, y1, x2, y2, height) { | ||
var lr = this.lastRectangle; | ||
if (lr && x1 === lr.x1 && x2 === lr.x2 && y1 === lr.y1 && y2 === lr.y2 && height === lr.height) { | ||
return this.cache; | ||
} | ||
this.lastRectangle = { | ||
x1: x1, | ||
y1: y1, | ||
x2: x2, | ||
y2: y2, | ||
height: height, | ||
}; | ||
// Is the rectangle axis aligned? | ||
if (!isAxisAligned(x1, y1, x2, y2)) | ||
throw new Error("sigma/quadtree.rectangle: shifted view is not yet implemented."); | ||
var collectedNodes = getNodesInAxisAlignedRectangleArea(MAX_LEVEL, this.data, this.containers, x1, y1, Math.abs(x1 - x2) || Math.abs(y1 - y2), height); | ||
this.cache = collectedNodes; | ||
return this.cache; | ||
}; | ||
return QuadTree; | ||
}()); | ||
exports.default = QuadTree; |
# Sigma v2 | ||
Nothing to see here... | ||
Sigma is a JavaScript library dedicated to graph drawing, mainly developed by [@jacomyal](https://github.com/jacomyal) and [@Yomguithereal](https://github.com/Yomguithereal). | ||
## Overview | ||
[sigmajs.org website](http://sigmajs.org) provides a global overview of sigma.js v1. | ||
As of version `v2`, `sigma` focuses on the management of graph display: layout, rendering, interaction... The graph model is managed in a separate library called [`graphology`](https://github.com/graphology/graphology), which is packed with convenience methods to manage graph data structures. | ||
A set of demo examples contain various use-cases that might help you understand how to use sigma v2 (read further below). | ||
### Status | ||
Sigma.js v2 is a major refactoring and is currently in version alpha. The stable version is `v1.2.x`. Although not yet finalized and official, v2 is already in use in production in some organizations. | ||
## Installation | ||
Sigma is a javascript library, it is available in `npm` package manager. | ||
npm install sigma | ||
## Examples | ||
A development server can be spawned locally to view the examples. Visit `localhost:8000` after executing the following commands: | ||
npm install | ||
npm run examples | ||
You can play around with the files in directory `examples`, the web pages are live-reloaded whenever the code gets changed. | ||
## Contributing | ||
You can contribute by submitting [issues tickets](http://github.com/jacomyal/sigma.js/issues) and proposing [pull requests](http://github.com/jacomyal/sigma.js/pulls). Make sure that tests and linting pass before submitting any pull request. |
@@ -1,22 +0,24 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _events = require('events'); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js Renderer Class | ||
* ======================== | ||
* | ||
* Abstract classes extended by all of sigma's renderers. | ||
*/ | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js Renderer Class | ||
* ======================== | ||
* | ||
* Abstract classes extended by all of sigma's renderers. | ||
*/ | ||
var events_1 = require("events"); | ||
/** | ||
* Renderer class. | ||
@@ -26,14 +28,9 @@ * | ||
*/ | ||
var Renderer = function (_EventEmitter) { | ||
_inherits(Renderer, _EventEmitter); | ||
function Renderer() { | ||
_classCallCheck(this, Renderer); | ||
return _possibleConstructorReturn(this, (Renderer.__proto__ || Object.getPrototypeOf(Renderer)).apply(this, arguments)); | ||
} | ||
return Renderer; | ||
}(_events.EventEmitter); | ||
exports.default = Renderer; | ||
var Renderer = /** @class */ (function (_super) { | ||
__extends(Renderer, _super); | ||
function Renderer() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return Renderer; | ||
}(events_1.EventEmitter)); | ||
exports.default = Renderer; |
@@ -1,18 +0,6 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = drawHover; | ||
var _node = require('./node'); | ||
var _node2 = _interopRequireDefault(_node); | ||
var _label = require('./label'); | ||
var _label2 = _interopRequireDefault(_label); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -25,150 +13,34 @@ * Sigma.js Canvas Renderer Hover Component | ||
*/ | ||
function drawHover(context, data) { | ||
context.font = '14px arial'; | ||
// Then we draw the label background | ||
context.beginPath(); | ||
context.fillStyle = '#fff'; | ||
context.shadowOffsetX = 0; | ||
context.shadowOffsetY = 0; | ||
context.shadowBlur = 8; | ||
context.shadowColor = '#000'; | ||
var textWidth = context.measureText(data.label).width; | ||
var x = Math.round(data.x - 14 / 2 - 2), | ||
y = Math.round(data.y - 14 / 2 - 2), | ||
w = Math.round(textWidth + 14 / 2 + data.size + 9), | ||
h = Math.round(14 + 4), | ||
e = Math.round(14 / 2 + 2); | ||
context.moveTo(x, y + e); | ||
context.moveTo(x, y + e); | ||
context.arcTo(x, y, x + e, y, e); | ||
context.lineTo(x + w, y); | ||
context.lineTo(x + w, y + h); | ||
context.lineTo(x + e, y + h); | ||
context.arcTo(x, y + h, x, y + h - e, e); | ||
context.lineTo(x, y + e); | ||
context.closePath(); | ||
context.fill(); | ||
context.shadowOffsetX = 0; | ||
context.shadowOffsetY = 0; | ||
context.shadowBlur = 0; | ||
// Then we need to draw the node | ||
(0, _node2.default)(context, data); | ||
// And finally we draw the label | ||
(0, _label2.default)(context, data); | ||
var node_1 = __importDefault(require("./node")); | ||
var label_1 = __importDefault(require("./label")); | ||
function drawHover(context, data, settings) { | ||
var size = settings.labelSize, font = settings.labelFont, weight = settings.labelWeight; | ||
context.font = weight + " " + size + "px " + font; | ||
// Then we draw the label background | ||
context.beginPath(); | ||
context.fillStyle = "#fff"; | ||
context.shadowOffsetX = 0; | ||
context.shadowOffsetY = 0; | ||
context.shadowBlur = 8; | ||
context.shadowColor = "#000"; | ||
var textWidth = context.measureText(data.label).width; | ||
var x = Math.round(data.x - size / 2 - 2), y = Math.round(data.y - size / 2 - 2), w = Math.round(textWidth + size / 2 + data.size + 9), h = Math.round(size + 4), e = Math.round(size / 2 + 2); | ||
context.moveTo(x, y + e); | ||
context.moveTo(x, y + e); | ||
context.arcTo(x, y, x + e, y, e); | ||
context.lineTo(x + w, y); | ||
context.lineTo(x + w, y + h); | ||
context.lineTo(x + e, y + h); | ||
context.arcTo(x, y + h, x, y + h - e, e); | ||
context.lineTo(x, y + e); | ||
context.closePath(); | ||
context.fill(); | ||
context.shadowOffsetX = 0; | ||
context.shadowOffsetY = 0; | ||
context.shadowBlur = 0; | ||
// Then we need to draw the node | ||
node_1.default(context, data); | ||
// And finally we draw the label | ||
label_1.default(context, data, settings); | ||
} | ||
// ;(function(undefined) { | ||
// 'use strict'; | ||
// if (typeof sigma === 'undefined') | ||
// throw 'sigma is not declared'; | ||
// // Initialize packages: | ||
// sigma.utils.pkg('sigma.canvas.hovers'); | ||
// /** | ||
// * This hover renderer will basically display the label with a background. | ||
// * | ||
// * @param {object} node The node object. | ||
// * @param {CanvasRenderingContext2D} context The canvas context. | ||
// * @param {configurable} settings The settings function. | ||
// */ | ||
// sigma.canvas.hovers.def = function(node, context, settings) { | ||
// var x, | ||
// y, | ||
// w, | ||
// h, | ||
// e, | ||
// fontStyle = settings('hoverFontStyle') || settings('fontStyle'), | ||
// prefix = settings('prefix') || '', | ||
// size = node[prefix + 'size'], | ||
// fontSize = (settings('labelSize') === 'fixed') ? | ||
// settings('defaultLabelSize') : | ||
// settings('labelSizeRatio') * size; | ||
// // Label background: | ||
// context.font = (fontStyle ? fontStyle + ' ' : '') + | ||
// fontSize + 'px ' + (settings('hoverFont') || settings('font')); | ||
// context.beginPath(); | ||
// context.fillStyle = settings('labelHoverBGColor') === 'node' ? | ||
// (node.color || settings('defaultNodeColor')) : | ||
// settings('defaultHoverLabelBGColor'); | ||
// if (node.label && settings('labelHoverShadow')) { | ||
// context.shadowOffsetX = 0; | ||
// context.shadowOffsetY = 0; | ||
// context.shadowBlur = 8; | ||
// context.shadowColor = settings('labelHoverShadowColor'); | ||
// } | ||
// if (node.label && typeof node.label === 'string') { | ||
// x = Math.round(node[prefix + 'x'] - fontSize / 2 - 2); | ||
// y = Math.round(node[prefix + 'y'] - fontSize / 2 - 2); | ||
// w = Math.round( | ||
// context.measureText(node.label).width + fontSize / 2 + size + 7 | ||
// ); | ||
// h = Math.round(fontSize + 4); | ||
// e = Math.round(fontSize / 2 + 2); | ||
// context.moveTo(x, y + e); | ||
// context.arcTo(x, y, x + e, y, e); | ||
// context.lineTo(x + w, y); | ||
// context.lineTo(x + w, y + h); | ||
// context.lineTo(x + e, y + h); | ||
// context.arcTo(x, y + h, x, y + h - e, e); | ||
// context.lineTo(x, y + e); | ||
// context.closePath(); | ||
// context.fill(); | ||
// context.shadowOffsetX = 0; | ||
// context.shadowOffsetY = 0; | ||
// context.shadowBlur = 0; | ||
// } | ||
// // Node border: | ||
// if (settings('borderSize') > 0) { | ||
// context.beginPath(); | ||
// context.fillStyle = settings('nodeBorderColor') === 'node' ? | ||
// (node.color || settings('defaultNodeColor')) : | ||
// settings('defaultNodeBorderColor'); | ||
// context.arc( | ||
// node[prefix + 'x'], | ||
// node[prefix + 'y'], | ||
// size + settings('borderSize'), | ||
// 0, | ||
// Math.PI * 2, | ||
// true | ||
// ); | ||
// context.closePath(); | ||
// context.fill(); | ||
// } | ||
// // Node: | ||
// var nodeRenderer = sigma.canvas.nodes[node.type] || sigma.canvas.nodes.def; | ||
// nodeRenderer(node, context, settings); | ||
// // Display the label: | ||
// if (node.label && typeof node.label === 'string') { | ||
// context.fillStyle = (settings('labelHoverColor') === 'node') ? | ||
// (node.color || settings('defaultNodeColor')) : | ||
// settings('defaultLabelHoverColor'); | ||
// context.fillText( | ||
// node.label, | ||
// Math.round(node[prefix + 'x'] + size + 3), | ||
// Math.round(node[prefix + 'y'] + fontSize / 3) | ||
// ); | ||
// } | ||
// }; | ||
// }).call(this); | ||
exports.default = drawHover; |
@@ -1,7 +0,3 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = drawLabel; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -13,7 +9,8 @@ * Sigma.js Canvas Renderer Label Component | ||
*/ | ||
function drawLabel(context, data) { | ||
context.fillStyle = '#000'; | ||
context.font = '14px arial'; | ||
context.fillText(data.label, data.x + data.size + 3, data.y + 14 / 3); | ||
} | ||
function drawLabel(context, data, settings) { | ||
var size = settings.labelSize, font = settings.labelFont, weight = settings.labelWeight; | ||
context.fillStyle = "#000"; | ||
context.font = weight + " " + size + "px " + font; | ||
context.fillText(data.label, data.x + data.size + 3, data.y + size / 3); | ||
} | ||
exports.default = drawLabel; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = drawNode; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -14,11 +10,9 @@ * Sigma.js Canvas Renderer Node Component | ||
var PI_TIMES_2 = Math.PI * 2; | ||
function drawNode(context, data) { | ||
context.fillStyle = data.color; | ||
context.beginPath(); | ||
context.arc(data.x, data.y, data.size, 0, PI_TIMES_2, true); | ||
context.closePath(); | ||
context.fill(); | ||
} | ||
context.fillStyle = data.color; | ||
context.beginPath(); | ||
context.arc(data.x, data.y, data.size, 0, PI_TIMES_2, true); | ||
context.closePath(); | ||
context.fill(); | ||
} | ||
exports.default = drawNode; |
@@ -1,9 +0,2 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.createElement = createElement; | ||
exports.getPixelRatio = getPixelRatio; | ||
exports.createNodeRescalingFunction = createNodeRescalingFunction; | ||
"use strict"; | ||
/** | ||
@@ -15,3 +8,20 @@ * Sigma.js Rendering Utils | ||
*/ | ||
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; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createNormalizationFunction = exports.getPixelRatio = exports.createElement = void 0; | ||
/** | ||
@@ -25,19 +35,17 @@ * Function used to create DOM elements easily. | ||
function createElement(tag, attributes) { | ||
var element = document.createElement(tag); | ||
if (!attributes) return element; | ||
for (var k in attributes) { | ||
if (k === 'style') { | ||
for (var s in attributes[k]) { | ||
element.style[s] = attributes[k][s]; | ||
} | ||
} else { | ||
element.setAttribute(k, attributes[k]); | ||
var element = document.createElement(tag); | ||
if (!attributes) | ||
return element; | ||
for (var k in attributes) { | ||
if (k === "style") { | ||
for (var s in attributes[k]) | ||
element.style[s] = attributes[k][s]; | ||
} | ||
else { | ||
element.setAttribute(k, attributes[k]); | ||
} | ||
} | ||
} | ||
return element; | ||
return element; | ||
} | ||
exports.createElement = createElement; | ||
/** | ||
@@ -49,67 +57,39 @@ * Function returning the browser's pixel ratio. | ||
function getPixelRatio() { | ||
var screen = window.screen; | ||
if (typeof screen.deviceXDPI !== 'undefined' && typeof screen.logicalXDPI !== 'undefined' && screen.deviceXDPI > screen.logicalXDPI) return screen.systemXDPI / screen.logicalXDPI;else if (typeof window.devicePixelRatio !== 'undefined') return window.devicePixelRatio; | ||
return 1; | ||
if (typeof window.devicePixelRatio !== "undefined") | ||
return window.devicePixelRatio; | ||
return 1; | ||
} | ||
exports.getPixelRatio = getPixelRatio; | ||
/** | ||
* Default rescale options. | ||
*/ | ||
var DEFAULT_NODE_RESCALE_OPTIONS = { | ||
mode: 'inside', | ||
margin: 0, | ||
minSize: 1, | ||
maxSize: 8 | ||
}; | ||
var DEFAULT_EDGE_RESCALE_OPTIONS = { | ||
minSize: 0.5, | ||
maxSize: 1 | ||
}; | ||
// TODO: should we let the user handle size through, for instance, d3 scales? | ||
// TODO: should we put the rescaling in the camera itself? | ||
/** | ||
* Factory returning a function rescaling the given node's position and/or size. | ||
* Factory returning a function normalizing the given node's position & size. | ||
* | ||
* @param {object} options - Options. | ||
* @param {object} extent - Extent of the graph. | ||
* @return {function} | ||
*/ | ||
function createNodeRescalingFunction(options, extent) { | ||
options = options || {}; | ||
var mode = options.mode || DEFAULT_NODE_RESCALE_OPTIONS.mode, | ||
height = options.height || 1, | ||
width = options.width || 1; | ||
var maxX = extent.maxX, | ||
maxY = extent.maxY, | ||
minX = extent.minX, | ||
minY = extent.minY; | ||
var hx = (maxX + minX) / 2, | ||
hy = (maxY + minY) / 2; | ||
var scale = mode === 'outside' ? Math.max(width / Math.max(maxX - minX, 1), height / Math.max(maxY - minY, 1)) : Math.min(width / Math.max(maxX - minX, 1), height / Math.max(maxY - minY, 1)); | ||
var fn = function fn(data) { | ||
return { | ||
x: (data.x - hx) * scale, | ||
y: (data.y - hy) * scale | ||
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, | ||
}; | ||
}; | ||
}; | ||
fn.inverse = function (data) { | ||
return { | ||
x: data.x / scale + hx, | ||
y: data.y / scale + hy | ||
// 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; | ||
}; | ||
}; | ||
return fn; | ||
} | ||
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; |
@@ -1,69 +0,44 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _glMatrix = require('gl-matrix'); | ||
var _renderer = require('../../renderer'); | ||
var _renderer2 = _interopRequireDefault(_renderer); | ||
var _camera = require('../../camera'); | ||
var _camera2 = _interopRequireDefault(_camera); | ||
var _mouse = require('../../captors/mouse'); | ||
var _mouse2 = _interopRequireDefault(_mouse); | ||
var _quadtree = require('../../quadtree'); | ||
var _quadtree2 = _interopRequireDefault(_quadtree); | ||
var _node = require('./programs/node'); | ||
var _node2 = _interopRequireDefault(_node); | ||
var _edge = require('./programs/edge'); | ||
var _edge2 = _interopRequireDefault(_edge); | ||
var _label = require('../canvas/components/label'); | ||
var _label2 = _interopRequireDefault(_label); | ||
var _hover = require('../canvas/components/hover'); | ||
var _hover2 = _interopRequireDefault(_hover); | ||
var _utils = require('../../utils'); | ||
var _utils2 = require('../utils'); | ||
var _utils3 = require('./utils'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js WebGL Renderer | ||
* ======================== | ||
* | ||
* File implementing sigma's WebGL Renderer. | ||
*/ | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js WebGL Renderer | ||
* ======================== | ||
* | ||
* File implementing sigma's WebGL Renderer. | ||
*/ | ||
var extent_1 = __importDefault(require("graphology-metrics/extent")); | ||
var is_graph_1 = __importDefault(require("graphology-utils/is-graph")); | ||
var renderer_1 = __importDefault(require("../../renderer")); | ||
var camera_1 = __importDefault(require("../../camera")); | ||
var mouse_1 = __importDefault(require("../../captors/mouse")); | ||
var quadtree_1 = __importDefault(require("../../quadtree")); | ||
var display_data_1 = require("../display-data"); | ||
var utils_1 = require("../../utils"); | ||
var utils_2 = require("../utils"); | ||
var utils_3 = require("./utils"); | ||
var labels_1 = require("../../heuristics/labels"); | ||
var z_index_1 = require("../../heuristics/z-index"); | ||
var settings_1 = require("./settings"); | ||
var nodeExtent = extent_1.default.nodeExtent, edgeExtent = extent_1.default.edgeExtent; | ||
/** | ||
* Constants. | ||
*/ | ||
var PIXEL_RATIO = (0, _utils2.getPixelRatio)(); | ||
var WEBGL_OVERSAMPLING_RATIO = (0, _utils2.getPixelRatio)(); | ||
var PIXEL_RATIO = utils_2.getPixelRatio(); | ||
var WEBGL_OVERSAMPLING_RATIO = utils_2.getPixelRatio(); | ||
/** | ||
@@ -73,124 +48,93 @@ * Main class. | ||
* @constructor | ||
* @param {HTMLElement} container - The graph's container. | ||
* @param {Graph} graph - Graph to render. | ||
* @param {HTMLElement} container - DOM container in which to render. | ||
* @param {object} settings - Optional settings. | ||
*/ | ||
var WebGLRenderer = function (_Renderer) { | ||
_inherits(WebGLRenderer, _Renderer); | ||
function WebGLRenderer(container) { | ||
_classCallCheck(this, WebGLRenderer); | ||
// Validating | ||
var _this = _possibleConstructorReturn(this, (WebGLRenderer.__proto__ || Object.getPrototypeOf(WebGLRenderer)).call(this)); | ||
if (!(container instanceof HTMLElement)) throw new Error('sigma/renderers/webgl: container should be an html element.'); | ||
// Properties | ||
_this.sigma = null; | ||
_this.captors = {}; | ||
_this.container = container; | ||
_this.elements = {}; | ||
_this.contexts = {}; | ||
_this.listeners = {}; | ||
_this.quadtree = new _quadtree2.default(); | ||
_this.nodeArray = null; | ||
_this.nodeIndicesArray = null; | ||
_this.nodeOrder = {}; | ||
// TODO: this could be improved by key => index => floatArray | ||
_this.nodeDataCache = {}; | ||
_this.edgeArray = null; | ||
_this.edgeIndicesArray = null; | ||
_this.edgeOrder = {}; | ||
// TODO: if we drop size scaling => this should become "rescalingFunction" | ||
_this.nodeRescalingFunction = null; | ||
// Starting dimensions | ||
_this.width = 0; | ||
_this.height = 0; | ||
// State | ||
_this.highlightedNodes = new Set(); | ||
_this.hoveredNode = null; | ||
_this.wasRenderedInThisFrame = false; | ||
_this.renderFrame = null; | ||
_this.renderHighlightedNodesFrame = null; | ||
_this.needToProcess = false; | ||
_this.needToSoftProcess = false; | ||
_this.pixel = new Uint8Array(4); | ||
// Initializing contexts | ||
_this.createContext('edges'); | ||
_this.createContext('nodes'); | ||
_this.createContext('labels', false); | ||
_this.createContext('hovers', false); | ||
_this.createContext('mouse', false); | ||
// Blending | ||
var gl = _this.contexts.nodes; | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.enable(gl.BLEND); | ||
gl = _this.contexts.edges; | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.enable(gl.BLEND); | ||
// Loading programs | ||
_this.nodePrograms = { | ||
def: new _node2.default(_this.contexts.nodes) | ||
}; | ||
_this.edgePrograms = { | ||
def: new _edge2.default(_this.contexts.edges) | ||
}; | ||
// Initial resize | ||
_this.resize(); | ||
// Initializing the camera | ||
_this.camera = new _camera2.default({ | ||
width: _this.width, | ||
height: _this.height | ||
}); | ||
// Binding camera events | ||
_this.bindCameraHandlers(); | ||
// Initializing captors | ||
_this.captors = { | ||
mouse: new _mouse2.default(_this.elements.mouse, _this.camera) | ||
}; | ||
// Binding event handlers | ||
_this.bindEventHandlers(); | ||
return _this; | ||
} | ||
/**--------------------------------------------------------------------------- | ||
* Internal methods. | ||
**--------------------------------------------------------------------------- | ||
*/ | ||
/** | ||
* Method used to test a pixel of the given context. | ||
* | ||
* @param {WebGLContext} gl - Context. | ||
* @param {number} x - Client x. | ||
* @param {number} y - Client y. | ||
* @return {boolean} | ||
*/ | ||
_createClass(WebGLRenderer, [{ | ||
key: 'testPixel', | ||
value: function testPixel(gl, x, y) { | ||
(0, _utils3.extractPixel)(gl, x * WEBGL_OVERSAMPLING_RATIO, (this.height - y) * WEBGL_OVERSAMPLING_RATIO, this.pixel); | ||
return this.pixel[3] !== 0; | ||
var WebGLRenderer = /** @class */ (function (_super) { | ||
__extends(WebGLRenderer, _super); | ||
function WebGLRenderer(graph, container, settings) { | ||
if (settings === void 0) { settings = {}; } | ||
var _this = _super.call(this) || this; | ||
_this.captors = {}; | ||
_this.elements = {}; | ||
_this.contexts = {}; | ||
_this.listeners = {}; | ||
_this.quadtree = new quadtree_1.default(); | ||
_this.nodeDataCache = {}; | ||
_this.edgeDataCache = {}; | ||
_this.nodeExtent = null; | ||
_this.edgeExtent = null; | ||
_this.normalizationFunction = null; | ||
// Starting dimensions | ||
_this.width = 0; | ||
_this.height = 0; | ||
// State | ||
_this.highlightedNodes = new Set(); | ||
_this.displayedLabels = new Set(); | ||
_this.hoveredNode = null; | ||
_this.wasRenderedInThisFrame = false; | ||
_this.renderFrame = null; | ||
_this.renderHighlightedNodesFrame = null; | ||
_this.needToProcess = false; | ||
_this.needToSoftProcess = false; | ||
// programs | ||
_this.nodePrograms = {}; | ||
_this.edgePrograms = {}; | ||
_this.settings = utils_1.assign({}, settings_1.WEBGL_RENDERER_DEFAULT_SETTINGS, settings); | ||
settings_1.validateWebglRendererSettings(_this.settings); | ||
// Validating | ||
if (!is_graph_1.default(graph)) | ||
throw new Error("sigma/renderers/webgl: invalid graph instance."); | ||
if (!(container instanceof HTMLElement)) | ||
throw new Error("sigma/renderers/webgl: container should be an html element."); | ||
// Properties | ||
_this.graph = graph; | ||
_this.container = container; | ||
_this.initializeCache(); | ||
// Initializing contexts | ||
_this.createContext("edges"); | ||
_this.createContext("edgeLabels", false); | ||
_this.createContext("nodes"); | ||
_this.createContext("labels", false); | ||
_this.createContext("hovers", false); | ||
_this.createContext("mouse", false); | ||
// Blending | ||
var gl = _this.contexts.nodes; | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.enable(gl.BLEND); | ||
gl = _this.contexts.edges; | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.enable(gl.BLEND); | ||
// Loading programs | ||
for (var type in _this.settings.nodeProgramClasses) { | ||
var NodeProgramClass = _this.settings.nodeProgramClasses[type]; | ||
_this.nodePrograms[type] = new NodeProgramClass(_this.contexts.nodes); | ||
} | ||
for (var type in _this.settings.edgeProgramClasses) { | ||
var EdgeProgramClass = _this.settings.edgeProgramClasses[type]; | ||
_this.edgePrograms[type] = new EdgeProgramClass(_this.contexts.edges); | ||
} | ||
// Initial resize | ||
_this.resize(); | ||
// Initializing the camera | ||
_this.camera = new camera_1.default(); | ||
// Binding camera events | ||
_this.bindCameraHandlers(); | ||
// Initializing captors | ||
_this.captors = { | ||
mouse: new mouse_1.default(_this.elements.mouse, _this.camera), | ||
}; | ||
// Binding event handlers | ||
_this.bindEventHandlers(); | ||
// Binding graph handlers | ||
_this.bindGraphHandlers(); | ||
// Processing data for the first time & render | ||
_this.process(); | ||
_this.render(); | ||
return _this; | ||
} | ||
/**--------------------------------------------------------------------------- | ||
* Internal methods. | ||
**--------------------------------------------------------------------------- | ||
*/ | ||
/** | ||
@@ -204,31 +148,48 @@ * Internal function used to create a canvas context and add the relevant | ||
*/ | ||
}, { | ||
key: 'createContext', | ||
value: function createContext(id) { | ||
var webgl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
var element = (0, _utils2.createElement)('canvas', { | ||
class: 'sigma-' + id, | ||
style: { | ||
position: 'absolute' | ||
WebGLRenderer.prototype.createContext = function (id, webgl) { | ||
if (webgl === void 0) { webgl = true; } | ||
var element = utils_2.createElement("canvas", { | ||
class: "sigma-" + id, | ||
style: { | ||
position: "absolute", | ||
}, | ||
}); | ||
this.elements[id] = element; | ||
this.container.appendChild(element); | ||
var contextOptions = { | ||
preserveDrawingBuffer: false, | ||
antialias: false, | ||
}; | ||
var context; | ||
if (webgl) { | ||
// First we try webgl2 for an easy performance boost | ||
context = element.getContext("webgl2", contextOptions); | ||
// Else we fall back to webgl | ||
if (!context) | ||
context = element.getContext("webgl", contextOptions); | ||
// Edge, I am looking right at you... | ||
if (!context) | ||
context = element.getContext("experimental-webgl", contextOptions); | ||
} | ||
}); | ||
this.elements[id] = element; | ||
this.container.appendChild(element); | ||
var contextOptions = { | ||
preserveDrawingBuffer: true, | ||
antialias: false | ||
}; | ||
var context = element.getContext(webgl ? 'webgl' : '2d', contextOptions); | ||
this.contexts[id] = context; | ||
return this; | ||
} | ||
else { | ||
context = element.getContext("2d", contextOptions); | ||
} | ||
this.contexts[id] = context; | ||
return this; | ||
}; | ||
/** | ||
* Method used to initialize display data cache. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
WebGLRenderer.prototype.initializeCache = function () { | ||
var graph = this.graph; | ||
var nodes = graph.nodes(); | ||
for (var i = 0, l = nodes.length; i < l; i++) | ||
this.nodeDataCache[nodes[i]] = new display_data_1.NodeDisplayData(i, this.settings); | ||
var edges = graph.edges(); | ||
for (var i = 0, l = edges.length; i < l; i++) | ||
this.edgeDataCache[edges[i]] = new display_data_1.EdgeDisplayData(i, this.settings); | ||
}; | ||
/** | ||
* Method binding camera handlers. | ||
@@ -238,17 +199,10 @@ * | ||
*/ | ||
}, { | ||
key: 'bindCameraHandlers', | ||
value: function bindCameraHandlers() { | ||
var _this2 = this; | ||
this.listeners.camera = function () { | ||
_this2.scheduleRender(); | ||
}; | ||
this.camera.on('updated', this.listeners.camera); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.bindCameraHandlers = function () { | ||
var _this = this; | ||
this.listeners.camera = function () { | ||
_this.scheduleRender(); | ||
}; | ||
this.camera.on("updated", this.listeners.camera); | ||
return this; | ||
}; | ||
/** | ||
@@ -259,122 +213,93 @@ * Method binding event handlers. | ||
*/ | ||
}, { | ||
key: 'bindEventHandlers', | ||
value: function bindEventHandlers() { | ||
var _this3 = this; | ||
// Handling window resize | ||
this.listeners.handleResize = function () { | ||
_this3.needToSoftProcess = true; | ||
_this3.scheduleRender(); | ||
}; | ||
window.addEventListener('resize', this.listeners.handleResize); | ||
// Function checking if the mouse is on the given node | ||
var mouseIsOnNode = function mouseIsOnNode(mouseX, mouseY, nodeX, nodeY, size) { | ||
return mouseX > nodeX - size && mouseX < nodeX + size && mouseY > nodeY - size && mouseY < nodeY + size && Math.sqrt(Math.pow(mouseX - nodeX, 2) + Math.pow(mouseY - nodeY, 2)) < size; | ||
}; | ||
// Function returning the nodes in the mouse's quad | ||
var getQuadNodes = function getQuadNodes(mouseX, mouseY) { | ||
var mouseGraphPosition = _this3.camera.displayToGraph(mouseX, mouseY); | ||
return _this3.quadtree.point(mouseGraphPosition.x, mouseGraphPosition.y); | ||
}; | ||
// Handling mouse move | ||
this.listeners.handleMove = function (e) { | ||
// NOTE: for the canvas renderer, testing the pixel's alpha should | ||
// give some boost but this slows things down for WebGL empirically. | ||
// TODO: this should be a method from the camera (or can be passed to graph to display somehow) | ||
var sizeRatio = Math.pow(_this3.camera.getState().ratio, 0.5); | ||
var quadNodes = getQuadNodes(e.x, e.y); | ||
for (var i = 0, l = quadNodes.length; i < l; i++) { | ||
var node = quadNodes[i]; | ||
var data = _this3.nodeDataCache[node]; | ||
var pos = _this3.camera.graphToDisplay(data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) { | ||
_this3.hoveredNode = node; | ||
_this3.emit('overNode', { node: node }); | ||
return _this3.scheduleHighlightedNodesRender(); | ||
} | ||
} | ||
// Checking if the hovered node is still hovered | ||
if (_this3.hoveredNode) { | ||
var _data = _this3.nodeDataCache[_this3.hoveredNode]; | ||
var _pos = _this3.camera.graphToDisplay(_data.x, _data.y); | ||
var _size = _data.size / sizeRatio; | ||
if (!mouseIsOnNode(e.x, e.y, _pos.x, _pos.y, _size)) { | ||
_this3.hoveredNode = null; | ||
_this3.emit('outNode', { node: _this3.hoveredNode }); | ||
return _this3.scheduleHighlightedNodesRender(); | ||
} | ||
} | ||
}; | ||
// Handling down | ||
this.listeners.handleDown = function (e) { | ||
var sizeRatio = Math.pow(_this3.camera.getState().ratio, 0.5); | ||
var quadNodes = getQuadNodes(e.x, e.y); | ||
for (var i = 0, l = quadNodes.length; i < l; i++) { | ||
var node = quadNodes[i]; | ||
var data = _this3.nodeDataCache[node]; | ||
var pos = _this3.camera.graphToDisplay(data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) return _this3.emit('downNode', { node: node }); | ||
} | ||
}; | ||
// Handling click | ||
this.listeners.handleClick = function (e) { | ||
var sizeRatio = Math.pow(_this3.camera.getState().ratio, 0.5); | ||
var quadNodes = getQuadNodes(e.x, e.y); | ||
for (var i = 0, l = quadNodes.length; i < l; i++) { | ||
var node = quadNodes[i]; | ||
var data = _this3.nodeDataCache[node]; | ||
var pos = _this3.camera.graphToDisplay(data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) return _this3.emit('clickNode', { node: node }); | ||
} | ||
return _this3.emit('clickStage'); | ||
}; | ||
// TODO: optimize, we don't need to repeat collisions | ||
this.captors.mouse.on('mousemove', this.listeners.handleMove); | ||
this.captors.mouse.on('mousedown', this.listeners.handleDown); | ||
this.captors.mouse.on('click', this.listeners.handleClick); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.bindEventHandlers = function () { | ||
var _this = this; | ||
// Handling window resize | ||
this.listeners.handleResize = function () { | ||
_this.needToSoftProcess = true; | ||
_this.scheduleRender(); | ||
}; | ||
window.addEventListener("resize", this.listeners.handleResize); | ||
// Function checking if the mouse is on the given node | ||
var mouseIsOnNode = function (mouseX, mouseY, nodeX, nodeY, size) { | ||
return (mouseX > nodeX - size && | ||
mouseX < nodeX + size && | ||
mouseY > nodeY - size && | ||
mouseY < nodeY + size && | ||
Math.sqrt(Math.pow(mouseX - nodeX, 2) + Math.pow(mouseY - nodeY, 2)) < size); | ||
}; | ||
// Function returning the nodes in the mouse's quad | ||
var getQuadNodes = function (mouseX, mouseY) { | ||
var mouseGraphPosition = _this.camera.viewportToGraph(_this, mouseX, mouseY); | ||
// TODO: minus 1? lol | ||
return _this.quadtree.point(mouseGraphPosition.x, 1 - mouseGraphPosition.y); | ||
}; | ||
// Handling mouse move | ||
this.listeners.handleMove = function (e) { | ||
// NOTE: for the canvas renderer, testing the pixel's alpha should | ||
// give some boost but this slows things down for WebGL empirically. | ||
// TODO: this should be a method from the camera (or can be passed to graph to display somehow) | ||
var sizeRatio = Math.pow(_this.camera.getState().ratio, 0.5); | ||
var quadNodes = getQuadNodes(e.x, e.y); | ||
// We will hover the node whose center is closest to mouse | ||
var minDistance = Infinity, nodeToHover = null; | ||
for (var i = 0, l = quadNodes.length; i < l; i++) { | ||
var node = quadNodes[i]; | ||
var data = _this.nodeDataCache[node]; | ||
var pos = _this.camera.graphToViewport(_this, data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) { | ||
var distance = Math.sqrt(Math.pow(e.x - pos.x, 2) + Math.pow(e.y - pos.y, 2)); | ||
// TODO: sort by min size also for cases where center is the same | ||
if (distance < minDistance) { | ||
minDistance = distance; | ||
nodeToHover = node; | ||
} | ||
} | ||
} | ||
if (nodeToHover && _this.hoveredNode !== nodeToHover) { | ||
// Handling passing from one node to the other directly | ||
if (_this.hoveredNode !== null) | ||
_this.emit("leaveNode", { node: _this.hoveredNode }); | ||
_this.hoveredNode = nodeToHover; | ||
_this.emit("enterNode", { node: nodeToHover }); | ||
return _this.scheduleHighlightedNodesRender(); | ||
} | ||
// Checking if the hovered node is still hovered | ||
if (_this.hoveredNode) { | ||
var data = _this.nodeDataCache[_this.hoveredNode]; | ||
var pos = _this.camera.graphToViewport(_this, data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (!mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) { | ||
var node = _this.hoveredNode; | ||
_this.hoveredNode = null; | ||
_this.emit("leaveNode", { node: node }); | ||
return _this.scheduleHighlightedNodesRender(); | ||
} | ||
} | ||
}; | ||
// Handling click | ||
var createClickListener = function (eventType) { | ||
return function (e) { | ||
var sizeRatio = Math.pow(_this.camera.getState().ratio, 0.5); | ||
var quadNodes = getQuadNodes(e.x, e.y); | ||
for (var i = 0, l = quadNodes.length; i < l; i++) { | ||
var node = quadNodes[i]; | ||
var data = _this.nodeDataCache[node]; | ||
var pos = _this.camera.graphToViewport(_this, data.x, data.y); | ||
var size = data.size / sizeRatio; | ||
if (mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) | ||
return _this.emit(eventType + "Node", { node: node, captor: e, event: e }); | ||
} | ||
return _this.emit(eventType + "Stage", { event: e }); | ||
}; | ||
}; | ||
this.listeners.handleClick = createClickListener("click"); | ||
this.listeners.handleRightClick = createClickListener("rightClick"); | ||
this.listeners.handleDown = createClickListener("down"); | ||
this.captors.mouse.on("mousemove", this.listeners.handleMove); | ||
this.captors.mouse.on("click", this.listeners.handleClick); | ||
this.captors.mouse.on("rightClick", this.listeners.handleRightClick); | ||
this.captors.mouse.on("mousedown", this.listeners.handleDown); | ||
return this; | ||
}; | ||
/** | ||
@@ -385,34 +310,36 @@ * Method binding graph handlers | ||
*/ | ||
}, { | ||
key: 'bindGraphHandlers', | ||
value: function bindGraphHandlers() { | ||
var _this4 = this; | ||
var graph = this.sigma.getGraph(); | ||
this.listeners.graphUpdate = function () { | ||
_this4.needToProcess = true; | ||
_this4.scheduleRender(); | ||
}; | ||
this.listeners.softGraphUpdate = function () { | ||
_this4.needToSoftProcess = true; | ||
_this4.scheduleRender(); | ||
}; | ||
// TODO: bind this on composed state events | ||
// TODO: it could be possible to update only specific node etc. by holding | ||
// a fixed-size pool of updated items | ||
graph.on('nodeAdded', this.listeners.graphUpdate); | ||
graph.on('nodeDropped', this.listeners.graphUpdate); | ||
graph.on('nodeAttributesUpdated', this.listeners.softGraphUpdate); | ||
graph.on('edgeAdded', this.listeners.graphUpdate); | ||
graph.on('nodeDropped', this.listeners.graphUpdate); | ||
graph.on('edgeAttributesUpdated', this.listeners.softGraphUpdate); | ||
graph.on('cleared', this.listeners.graphUpdate); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.bindGraphHandlers = function () { | ||
var _this = this; | ||
var graph = this.graph; | ||
this.listeners.graphUpdate = function () { | ||
_this.needToProcess = true; | ||
_this.scheduleRender(); | ||
}; | ||
this.listeners.softGraphUpdate = function () { | ||
_this.needToSoftProcess = true; | ||
_this.scheduleRender(); | ||
}; | ||
this.listeners.addNodeGraphUpdate = function (e) { | ||
// Adding entry to cache | ||
_this.nodeDataCache[e.key] = new display_data_1.NodeDisplayData(graph.order - 1, _this.settings); | ||
_this.listeners.graphUpdate(); | ||
}; | ||
this.listeners.addEdgeGraphUpdate = function (e) { | ||
// Adding entry to cache | ||
_this.edgeDataCache[e.key] = new display_data_1.EdgeDisplayData(graph.size - 1, _this.settings); | ||
_this.listeners.graphUpdate(); | ||
}; | ||
// TODO: clean cache on drop! | ||
// TODO: bind this on composed state events | ||
// TODO: it could be possible to update only specific node etc. by holding | ||
// a fixed-size pool of updated items | ||
graph.on("nodeAdded", this.listeners.addNodeGraphUpdate); | ||
graph.on("nodeDropped", this.listeners.graphUpdate); | ||
graph.on("nodeAttributesUpdated", this.listeners.softGraphUpdate); | ||
graph.on("edgeAdded", this.listeners.addEdgeGraphUpdate); | ||
graph.on("nodeDropped", this.listeners.graphUpdate); | ||
graph.on("edgeAttributesUpdated", this.listeners.softGraphUpdate); | ||
graph.on("cleared", this.listeners.graphUpdate); | ||
return this; | ||
}; | ||
/** | ||
@@ -423,94 +350,64 @@ * Method used to process the whole graph's data. | ||
*/ | ||
}, { | ||
key: 'process', | ||
value: function process() { | ||
var keepArrays = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
var graph = this.sigma.getGraph(); | ||
// TODO: possible to index this somehow using two byte arrays or so | ||
var extent = this.sigma.getGraphExtent(); | ||
// Rescaling function | ||
this.nodeRescalingFunction = (0, _utils2.createNodeRescalingFunction)({ width: this.width, height: this.height }, extent); | ||
var minRescaled = this.nodeRescalingFunction({ | ||
x: extent.minX, | ||
y: extent.minY | ||
}); | ||
var maxRescaled = this.nodeRescalingFunction({ | ||
x: extent.maxX, | ||
y: extent.maxY | ||
}); | ||
this.quadtree.resize({ | ||
x: minRescaled.x, | ||
y: minRescaled.y, | ||
width: maxRescaled.x - minRescaled.x, | ||
height: maxRescaled.y - minRescaled.y | ||
}); | ||
this.nodeRescaleCache = {}; | ||
var nodeProgram = this.nodePrograms.def; | ||
if (!keepArrays) { | ||
this.nodeArray = new Float32Array(_node2.default.POINTS * _node2.default.ATTRIBUTES * graph.order); | ||
this.nodeOrder = {}; | ||
} | ||
var nodes = graph.nodes(); | ||
for (var i = 0, l = nodes.length; i < l; i++) { | ||
var node = nodes[i]; | ||
this.nodeOrder[node] = i; | ||
var data = this.sigma.getNodeData(node); | ||
var rescaledData = this.nodeRescalingFunction(data); | ||
// TODO: Optimize this to be save a loop and one object | ||
var displayData = (0, _utils.assign)({}, data, rescaledData); | ||
this.quadtree.add(node, displayData.x, displayData.y, displayData.size); | ||
this.nodeDataCache[node] = displayData; | ||
nodeProgram.process(this.nodeArray, displayData, i * _node2.default.POINTS * _node2.default.ATTRIBUTES); | ||
} | ||
var edgeProgram = this.edgePrograms.def; | ||
if (!keepArrays) { | ||
this.edgeArray = new Float32Array(_edge2.default.POINTS * _edge2.default.ATTRIBUTES * graph.size); | ||
this.edgeOrder = {}; | ||
} | ||
var edges = graph.edges(); | ||
for (var _i = 0, _l = edges.length; _i < _l; _i++) { | ||
var edge = edges[_i]; | ||
this.edgeOrder[edge] = _i; | ||
var _data2 = this.sigma.getEdgeData(edge), | ||
extremities = graph.extremities(edge), | ||
sourceData = this.nodeDataCache[extremities[0]], | ||
targetData = this.nodeDataCache[extremities[1]]; | ||
edgeProgram.process(this.edgeArray, sourceData, targetData, _data2, _i * _edge2.default.POINTS * _edge2.default.ATTRIBUTES); | ||
} | ||
// Computing edge indices if necessary | ||
if (!keepArrays && typeof edgeProgram.computeIndices === 'function') this.edgeIndicesArray = edgeProgram.computeIndices(this.edgeArray); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.process = function (keepArrays) { | ||
if (keepArrays === void 0) { keepArrays = false; } | ||
var graph = this.graph, settings = this.settings; | ||
// Clearing the quad | ||
this.quadtree.clear(); | ||
// Computing extents | ||
var nodeExtentProperties = ["x", "y"]; | ||
if (this.settings.zIndex) { | ||
nodeExtentProperties.push("z"); | ||
this.edgeExtent = edgeExtent(graph, ["z"]); | ||
} | ||
this.nodeExtent = nodeExtent(graph, nodeExtentProperties); | ||
// Rescaling function | ||
this.normalizationFunction = utils_2.createNormalizationFunction(this.nodeExtent); | ||
var nodeProgram = this.nodePrograms[this.settings.defaultNodeType]; | ||
if (!keepArrays) | ||
nodeProgram.allocate(graph.order); | ||
var nodes = graph.nodes(); | ||
// Handling node z-index | ||
// TODO: z-index needs us to compute display data before hand | ||
// TODO: remains to be seen if reducers are a good or bad thing and if we | ||
// should store display data in flat byte arrays indices | ||
if (this.settings.zIndex) | ||
nodes = z_index_1.zIndexOrdering(this.edgeExtent.z, function (node) { return graph.getNodeAttribute(node, "z"); }, nodes); | ||
for (var i = 0, l = nodes.length; i < l; i++) { | ||
var node = nodes[i]; | ||
var data = graph.getNodeAttributes(node); | ||
var displayData = this.nodeDataCache[node]; | ||
if (settings.nodeReducer) | ||
data = settings.nodeReducer(node, data); | ||
// TODO: should assign default also somewhere here if there is a reducer | ||
displayData.assign(data); | ||
this.normalizationFunction.applyTo(displayData); | ||
this.quadtree.add(node, displayData.x, 1 - displayData.y, displayData.size / this.width); | ||
nodeProgram.process(displayData, i); | ||
displayData.index = i; | ||
} | ||
nodeProgram.bufferData(); | ||
var edgeProgram = this.edgePrograms[this.settings.defaultEdgeType]; | ||
if (!keepArrays) | ||
edgeProgram.allocate(graph.size); | ||
var edges = graph.edges(); | ||
// Handling edge z-index | ||
if (this.settings.zIndex) | ||
edges = z_index_1.zIndexOrdering(this.edgeExtent.z, function (edge) { return graph.getEdgeAttribute(edge, "z"); }, edges); | ||
for (var i = 0, l = edges.length; i < l; i++) { | ||
var edge = edges[i]; | ||
var data = graph.getEdgeAttributes(edge); | ||
var displayData = this.edgeDataCache[edge]; | ||
if (settings.edgeReducer) | ||
data = settings.edgeReducer(edge, data); | ||
displayData.assign(data); | ||
var extremities = graph.extremities(edge), sourceData = this.nodeDataCache[extremities[0]], targetData = this.nodeDataCache[extremities[1]]; | ||
edgeProgram.process(sourceData, targetData, displayData, i); | ||
displayData.index = i; | ||
} | ||
// Computing edge indices if necessary | ||
if (!keepArrays && typeof edgeProgram.computeIndices === "function") | ||
edgeProgram.computeIndices(); | ||
edgeProgram.bufferData(); | ||
return this; | ||
}; | ||
/** | ||
@@ -521,16 +418,8 @@ * Method used to process a single node. | ||
*/ | ||
}, { | ||
key: 'processNode', | ||
value: function processNode(key) { | ||
var nodeProgram = this.nodePrograms.def; | ||
var data = this.sigma.getNodeData(key); | ||
nodeProgram.process(this.nodeArray, data, this.nodeOrder[key] * _node2.default.POINTS * _node2.default.ATTRIBUTES); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.processNode = function (key) { | ||
var nodeProgram = this.nodePrograms[this.settings.defaultNodeType]; | ||
var data = this.graph.getNodeAttributes(key); | ||
nodeProgram.process(data, this.nodeDataCache[key].index); | ||
return this; | ||
}; | ||
/** | ||
@@ -541,21 +430,9 @@ * Method used to process a single edge. | ||
*/ | ||
}, { | ||
key: 'processEdge', | ||
value: function processEdge(key) { | ||
var graph = this.sigma.getGraph(); | ||
var edgeProgram = this.edgePrograms.def; | ||
var data = this.sigma.getEdgeData(key), | ||
extremities = graph.extremities(key), | ||
sourceData = this.sigma.getNodeData(extremities[0]), | ||
targetData = this.sigma.getNodeData(extremities[1]); | ||
edgeProgram.process(this.edgeArray, sourceData, targetData, data, this.edgeOrder[key] * _edge2.default.POINTS * _edge2.default.ATTRIBUTES); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.processEdge = function (key) { | ||
var graph = this.graph; | ||
var edgeProgram = this.edgePrograms[this.settings.defaultEdgeType]; | ||
var data = graph.getEdgeAttributes(key), extremities = graph.extremities(key), sourceData = graph.getNodeAttributes(extremities[0]), targetData = graph.getNodeAttributes(extremities[1]); | ||
edgeProgram.process(sourceData, targetData, data, this.edgeDataCache[key].index); | ||
return this; | ||
}; | ||
/**--------------------------------------------------------------------------- | ||
@@ -565,26 +442,3 @@ * Public API. | ||
*/ | ||
/** | ||
* Method used to bind the renderer to a sigma instance. | ||
* | ||
* @param {Sigma} sigma - Target sigma instance. | ||
* @return {WebGLRenderer} | ||
*/ | ||
}, { | ||
key: 'bind', | ||
value: function bind(sigma) { | ||
// Binding instance | ||
this.sigma = sigma; | ||
this.bindGraphHandlers(); | ||
// Processing initial data | ||
this.process(); | ||
return this; | ||
} | ||
/** | ||
* Method returning the renderer's camera. | ||
@@ -594,9 +448,5 @@ * | ||
*/ | ||
}, { | ||
key: 'getCamera', | ||
value: function getCamera() { | ||
return this.camera; | ||
} | ||
WebGLRenderer.prototype.getCamera = function () { | ||
return this.camera; | ||
}; | ||
/** | ||
@@ -607,9 +457,5 @@ * Method returning the mouse captor. | ||
*/ | ||
}, { | ||
key: 'getMouseCaptor', | ||
value: function getMouseCaptor() { | ||
return this.captors.mouse; | ||
} | ||
WebGLRenderer.prototype.getMouseCaptor = function () { | ||
return this.captors.mouse; | ||
}; | ||
/** | ||
@@ -622,58 +468,46 @@ * Method used to resize the renderer. | ||
*/ | ||
}, { | ||
key: 'resize', | ||
value: function resize(width, height) { | ||
var previousWidth = this.width, | ||
previousHeight = this.height; | ||
if (arguments.length > 1) { | ||
this.width = width; | ||
this.height = height; | ||
} else { | ||
this.width = this.container.offsetWidth; | ||
this.height = this.container.offsetHeight; | ||
} | ||
// If nothing has changed, we can stop right here | ||
if (previousWidth === this.width && previousHeight === this.height) return this; | ||
// Resizing camera | ||
// TODO: maybe move this elsewhere | ||
if (this.camera) this.camera.resize({ width: this.width, height: this.height }); | ||
// Sizing dom elements | ||
for (var id in this.elements) { | ||
var element = this.elements[id]; | ||
element.style.width = this.width + 'px'; | ||
element.style.height = this.height + 'px'; | ||
} | ||
// Sizing contexts | ||
for (var _id in this.contexts) { | ||
var context = this.contexts[_id]; | ||
// Canvas contexts | ||
if (context.scale) { | ||
this.elements[_id].setAttribute('width', this.width * PIXEL_RATIO + 'px'); | ||
this.elements[_id].setAttribute('height', this.height * PIXEL_RATIO + 'px'); | ||
if (PIXEL_RATIO !== 1) context.scale(PIXEL_RATIO, PIXEL_RATIO); | ||
WebGLRenderer.prototype.resize = function (width, height) { | ||
var previousWidth = this.width, previousHeight = this.height; | ||
if (arguments.length > 1) { | ||
this.width = width; | ||
this.height = height; | ||
} | ||
// WebGL contexts | ||
else { | ||
this.elements[_id].setAttribute('width', this.width * WEBGL_OVERSAMPLING_RATIO + 'px'); | ||
this.elements[_id].setAttribute('height', this.height * WEBGL_OVERSAMPLING_RATIO + 'px'); | ||
} | ||
if (context.viewport) { | ||
context.viewport(0, 0, this.width * WEBGL_OVERSAMPLING_RATIO, this.height * WEBGL_OVERSAMPLING_RATIO); | ||
this.width = this.container.offsetWidth; | ||
this.height = this.container.offsetHeight; | ||
} | ||
} | ||
return this; | ||
} | ||
if (this.width === 0) | ||
throw new Error("sigma/renderers/webgl: container has no width."); | ||
if (this.height === 0) | ||
throw new Error("sigma/renderers/webgl: container has no height."); | ||
// If nothing has changed, we can stop right here | ||
if (previousWidth === this.width && previousHeight === this.height) | ||
return this; | ||
// Sizing dom elements | ||
for (var id in this.elements) { | ||
var element = this.elements[id]; | ||
element.style.width = this.width + "px"; | ||
element.style.height = this.height + "px"; | ||
} | ||
// Sizing contexts | ||
for (var id in this.contexts) { | ||
var context_1 = this.contexts[id]; | ||
// Canvas contexts | ||
if (context_1.scale) { | ||
this.elements[id].setAttribute("width", this.width * PIXEL_RATIO + "px"); | ||
this.elements[id].setAttribute("height", this.height * PIXEL_RATIO + "px"); | ||
if (PIXEL_RATIO !== 1) | ||
context_1.scale(PIXEL_RATIO, PIXEL_RATIO); | ||
} | ||
// WebGL contexts | ||
else { | ||
this.elements[id].setAttribute("width", this.width * WEBGL_OVERSAMPLING_RATIO + "px"); | ||
this.elements[id].setAttribute("height", this.height * WEBGL_OVERSAMPLING_RATIO + "px"); | ||
} | ||
if (context_1.viewport) { | ||
context_1.viewport(0, 0, this.width * WEBGL_OVERSAMPLING_RATIO, this.height * WEBGL_OVERSAMPLING_RATIO); | ||
} | ||
} | ||
return this; | ||
}; | ||
/** | ||
@@ -684,21 +518,10 @@ * Method used to clear the canvases. | ||
*/ | ||
}, { | ||
key: 'clear', | ||
value: function clear() { | ||
var context = this.contexts.nodes; | ||
context.clear(context.COLOR_BUFFER_BIT); | ||
context = this.contexts.edges; | ||
context.clear(context.COLOR_BUFFER_BIT); | ||
context = this.contexts.labels; | ||
context.clearRect(0, 0, this.width, this.height); | ||
context = this.contexts.hovers; | ||
context.clearRect(0, 0, this.width, this.height); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.clear = function () { | ||
this.contexts.nodes.clear(this.contexts.nodes.COLOR_BUFFER_BIT); | ||
this.contexts.edges.clear(this.contexts.edges.COLOR_BUFFER_BIT); | ||
this.contexts.labels.clearRect(0, 0, this.width, this.height); | ||
this.contexts.hovers.clearRect(0, 0, this.width, this.height); | ||
this.contexts.edgeLabels.clearRect(0, 0, this.width, this.height); | ||
return this; | ||
}; | ||
/** | ||
@@ -709,194 +532,215 @@ * Method used to render. | ||
*/ | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
// If a render was scheduled, we cancel it | ||
if (this.renderFrame) { | ||
cancelAnimationFrame(this.renderFrame); | ||
this.renderFrame = null; | ||
this.needToProcess = false; | ||
this.needToSoftProcess = false; | ||
} | ||
// First we need to resize | ||
this.resize(); | ||
// Clearing the canvases | ||
this.clear(); | ||
// Then we need to extract a matrix from the camera | ||
var cameraState = this.camera.getState(), | ||
cameraMatrix = (0, _utils3.matrixFromCamera)(cameraState); | ||
var translation = _glMatrix.mat3.fromTranslation(_glMatrix.mat3.create(), [this.width / 2, this.height / 2]); | ||
_glMatrix.mat3.multiply(cameraMatrix, translation, cameraMatrix); | ||
var program = void 0, | ||
gl = void 0; | ||
// Drawing nodes | ||
gl = this.contexts.nodes; | ||
program = this.nodePrograms.def; | ||
// TODO: should probably use another name for the `program` abstraction | ||
program.render(gl, this.nodeArray, { | ||
matrix: cameraMatrix, | ||
width: this.width, | ||
height: this.height, | ||
ratio: cameraState.ratio, | ||
nodesPowRatio: 0.5, | ||
scalingRatio: WEBGL_OVERSAMPLING_RATIO | ||
}); | ||
// Drawing edges | ||
gl = this.contexts.edges; | ||
program = this.edgePrograms.def; | ||
program.render(gl, this.edgeArray, { | ||
matrix: cameraMatrix, | ||
width: this.width, | ||
height: this.height, | ||
ratio: cameraState.ratio, | ||
edgesPowRatio: 0.5, | ||
scalingRatio: WEBGL_OVERSAMPLING_RATIO, | ||
indices: this.edgeIndicesArray | ||
}); | ||
// Drawing labels | ||
// TODO: POW RATIO is currently default 0.5 and harcoded | ||
var nodes = this.sigma.getGraph().nodes(), | ||
context = this.contexts.labels; | ||
var sizeRatio = Math.pow(cameraState.ratio, 0.5); | ||
for (var i = 0, l = nodes.length; i < l; i++) { | ||
var data = this.nodeDataCache[nodes[i]]; | ||
var _camera$graphToDispla = this.camera.graphToDisplay(data.x, data.y), | ||
x = _camera$graphToDispla.x, | ||
y = _camera$graphToDispla.y; | ||
// TODO: we can cache the labels we need to render until the camera's ratio changes | ||
var size = data.size / sizeRatio; | ||
// TODO: this is the label threshold hardcoded | ||
if (size < 8) continue; | ||
(0, _label2.default)(context, { | ||
label: data.label, | ||
size: size, | ||
x: x, | ||
y: y | ||
WebGLRenderer.prototype.render = function () { | ||
// If a render was scheduled, we cancel it | ||
if (this.renderFrame) { | ||
cancelAnimationFrame(this.renderFrame); | ||
this.renderFrame = null; | ||
this.needToProcess = false; | ||
this.needToSoftProcess = false; | ||
} | ||
// First we need to resize | ||
this.resize(); | ||
// Clearing the canvases | ||
this.clear(); | ||
// If we have no nodes we can stop right there | ||
if (!this.graph.order) | ||
return this; | ||
// TODO: improve this heuristic or move to the captor itself? | ||
var moving = this.camera.isAnimated() || | ||
this.captors.mouse.isMoving || | ||
this.captors.mouse.hasDragged || | ||
this.captors.mouse.wheelLock; | ||
// Then we need to extract a matrix from the camera | ||
var cameraState = this.camera.getState(), cameraMatrix = utils_3.matrixFromCamera(cameraState, { | ||
width: this.width, | ||
height: this.height, | ||
}); | ||
} | ||
// Rendering highlighted nodes | ||
this.renderHighlightedNodes(); | ||
return this; | ||
} | ||
var program; | ||
// Drawing nodes | ||
program = this.nodePrograms[this.settings.defaultNodeType]; | ||
program.render({ | ||
matrix: cameraMatrix, | ||
width: this.width, | ||
height: this.height, | ||
ratio: cameraState.ratio, | ||
nodesPowRatio: 0.5, | ||
scalingRatio: WEBGL_OVERSAMPLING_RATIO, | ||
}); | ||
// Drawing edges | ||
if (!this.settings.hideEdgesOnMove || !moving) { | ||
program = this.edgePrograms[this.settings.defaultEdgeType]; | ||
program.render({ | ||
matrix: cameraMatrix, | ||
width: this.width, | ||
height: this.height, | ||
ratio: cameraState.ratio, | ||
nodesPowRatio: 0.5, | ||
edgesPowRatio: 0.5, | ||
scalingRatio: WEBGL_OVERSAMPLING_RATIO, | ||
}); | ||
} | ||
// Do not display labels on move per setting | ||
if (this.settings.hideLabelsOnMove && moving) | ||
return this; | ||
this.renderLabels(); | ||
this.renderEdgeLabels(); | ||
this.renderHighlightedNodes(); | ||
return this; | ||
}; | ||
/** | ||
* Method used to render the highlighted nodes. | ||
* Method used to render labels. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
}, { | ||
key: 'renderHighlightedNodes', | ||
value: function renderHighlightedNodes() { | ||
var _this5 = this; | ||
var camera = this.camera; | ||
var sizeRatio = Math.pow(camera.getState().ratio, 0.5); | ||
var context = this.contexts.hovers; | ||
// Clearing | ||
context.clearRect(0, 0, this.width, this.height); | ||
// Rendering | ||
var render = function render(node) { | ||
var data = _this5.nodeDataCache[node]; | ||
var _camera$graphToDispla2 = camera.graphToDisplay(data.x, data.y), | ||
x = _camera$graphToDispla2.x, | ||
y = _camera$graphToDispla2.y; | ||
var size = data.size / sizeRatio; | ||
(0, _hover2.default)(context, { | ||
label: data.label, | ||
color: data.color, | ||
size: size, | ||
x: x, | ||
y: y | ||
WebGLRenderer.prototype.renderLabels = function () { | ||
if (!this.settings.renderLabels) | ||
return this; | ||
var cameraState = this.camera.getState(); | ||
// Finding visible nodes to display their labels | ||
var visibleNodes; | ||
if (cameraState.ratio >= 1) { | ||
// Camera is unzoomed so no need to ask the quadtree for visible nodes | ||
visibleNodes = this.graph.nodes(); | ||
} | ||
else { | ||
// Let's ask the quadtree | ||
var viewRectangle = this.camera.viewRectangle(this); | ||
visibleNodes = this.quadtree.rectangle(viewRectangle.x1, 1 - viewRectangle.y1, viewRectangle.x2, 1 - viewRectangle.y2, viewRectangle.height); | ||
} | ||
// Selecting labels to draw | ||
var gridSettings = this.settings.labelGrid; | ||
var labelsToDisplay = labels_1.labelsToDisplayFromGrid({ | ||
cache: this.nodeDataCache, | ||
camera: this.camera, | ||
cell: gridSettings.cell, | ||
dimensions: this, | ||
displayedLabels: this.displayedLabels, | ||
fontSize: this.settings.labelSize, | ||
graph: this.graph, | ||
renderedSizeThreshold: gridSettings.renderedSizeThreshold, | ||
visibleNodes: visibleNodes, | ||
}); | ||
}; | ||
if (this.hoveredNode) render(this.hoveredNode); | ||
this.highlightedNodes.forEach(render); | ||
} | ||
// Drawing labels | ||
var context = this.contexts.labels; | ||
var sizeRatio = Math.pow(cameraState.ratio, 0.5); | ||
for (var i = 0, l = labelsToDisplay.length; i < l; i++) { | ||
var data = this.nodeDataCache[labelsToDisplay[i]]; | ||
var _a = this.camera.graphToViewport(this, data.x, data.y), x = _a.x, y = _a.y; | ||
// TODO: we can cache the labels we need to render until the camera's ratio changes | ||
// TODO: this should be computed in the canvas components? | ||
var size = data.size / sizeRatio; | ||
this.settings.labelRenderer(context, { | ||
key: labelsToDisplay[i], | ||
label: data.label, | ||
size: size, | ||
x: x, | ||
y: y, | ||
}, this.settings); | ||
} | ||
// Caching visible nodes and displayed labels | ||
this.displayedLabels = new Set(labelsToDisplay); | ||
return this; | ||
}; | ||
/** | ||
* Method used to schedule a render. | ||
* Method used to render edge labels, based on which node labels were | ||
* rendered. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
}, { | ||
key: 'scheduleRender', | ||
value: function scheduleRender() { | ||
var _this6 = this; | ||
// If we did not render in this frame yet | ||
// if (!this.wasRenderedInThisFrame) { | ||
// // Do we need to process data? | ||
// if (this.needToProcess || this.needToSoftProcess) | ||
// this.process(this.needToSoftProcess); | ||
// // Resetting state | ||
// this.renderFrame = null; | ||
// this.needToProcess = false; | ||
// this.needToSoftProcess = false; | ||
// this.render(); | ||
// this.wasRenderedInThisFrame = true; | ||
// requestAnimationFrame(() => { | ||
// this.wasRenderedInThisFrame = false; | ||
// }); | ||
// return this; | ||
// } | ||
// A frame is already scheduled | ||
if (this.renderFrame) return this; | ||
// Let's schedule a frame | ||
this.renderFrame = requestAnimationFrame(function () { | ||
// Do we need to process data? | ||
if (_this6.needToProcess || _this6.needToSoftProcess) _this6.process(_this6.needToSoftProcess); | ||
// Resetting state | ||
_this6.renderFrame = null; | ||
_this6.needToProcess = false; | ||
_this6.needToSoftProcess = false; | ||
WebGLRenderer.prototype.renderEdgeLabels = function () { | ||
if (!this.settings.renderEdgeLabels) | ||
return this; | ||
var cameraState = this.camera.getState(); | ||
var sizeRatio = Math.pow(cameraState.ratio, 0.5); | ||
var context = this.contexts.edgeLabels; | ||
// Clearing | ||
context.clearRect(0, 0, this.width, this.height); | ||
var edgeLabelsToDisplay = labels_1.edgeLabelsToDisplayFromNodes({ | ||
graph: this.graph, | ||
hoveredNode: this.hoveredNode, | ||
displayedNodeLabels: this.displayedLabels, | ||
highlightedNodes: this.highlightedNodes, | ||
}); | ||
for (var i = 0, l = edgeLabelsToDisplay.length; i < l; i++) { | ||
var edge = edgeLabelsToDisplay[i], extremities = this.graph.extremities(edge), sourceData = this.nodeDataCache[extremities[0]], targetData = this.nodeDataCache[extremities[1]], edgeData = this.edgeDataCache[edgeLabelsToDisplay[i]]; | ||
var _a = this.camera.graphToViewport(this, sourceData.x, sourceData.y), sourceX = _a.x, sourceY = _a.y; | ||
var _b = this.camera.graphToViewport(this, targetData.x, targetData.y), targetX = _b.x, targetY = _b.y; | ||
// TODO: we can cache the labels we need to render until the camera's ratio changes | ||
// TODO: this should be computed in the canvas components? | ||
var size = edgeData.size / sizeRatio; | ||
this.settings.edgeLabelRenderer(context, { | ||
key: edge, | ||
label: edgeData.label, | ||
color: edgeData.color, | ||
size: size, | ||
}, { | ||
key: extremities[0], | ||
x: sourceX, | ||
y: sourceY, | ||
}, { | ||
key: extremities[1], | ||
x: targetX, | ||
y: targetY, | ||
}, this.settings); | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Method used to render the highlighted nodes. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
WebGLRenderer.prototype.renderHighlightedNodes = function () { | ||
var _this = this; | ||
var camera = this.camera; | ||
var sizeRatio = Math.pow(camera.getState().ratio, 0.5); | ||
var context = this.contexts.hovers; | ||
// Clearing | ||
context.clearRect(0, 0, this.width, this.height); | ||
// Rendering | ||
_this6.render(); | ||
}); | ||
} | ||
var render = function (node) { | ||
var data = _this.nodeDataCache[node]; | ||
var _a = camera.graphToViewport(_this, data.x, data.y), x = _a.x, y = _a.y; | ||
var size = data.size / sizeRatio; | ||
_this.settings.hoverRenderer(context, { | ||
key: node, | ||
label: data.label, | ||
color: data.color, | ||
size: size, | ||
x: x, | ||
y: y, | ||
}, _this.settings); | ||
}; | ||
if (this.hoveredNode) | ||
render(this.hoveredNode); | ||
this.highlightedNodes.forEach(render); | ||
}; | ||
/** | ||
* Method used to schedule a render. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
WebGLRenderer.prototype.scheduleRender = function () { | ||
var _this = this; | ||
// A frame is already scheduled | ||
if (this.renderFrame) | ||
return this; | ||
// Let's schedule a frame | ||
this.renderFrame = requestAnimationFrame(function () { | ||
// Do we need to process data? | ||
if (_this.needToProcess) { | ||
_this.process(); | ||
} | ||
else if (_this.needToSoftProcess) { | ||
_this.process(true); | ||
} | ||
// Resetting state | ||
_this.renderFrame = null; | ||
_this.needToProcess = false; | ||
_this.needToSoftProcess = false; | ||
// Rendering | ||
_this.render(); | ||
}); | ||
}; | ||
/** | ||
* Method used to schedule a hover render. | ||
@@ -906,21 +750,25 @@ * | ||
*/ | ||
}, { | ||
key: 'scheduleHighlightedNodesRender', | ||
value: function scheduleHighlightedNodesRender() { | ||
var _this7 = this; | ||
if (this.renderHighlightedNodesFrame || this.renderFrame) return this; | ||
this.renderHighlightedNodesFrame = requestAnimationFrame(function () { | ||
// Resetting state | ||
_this7.renderHighlightedNodesFrame = null; | ||
// Rendering | ||
_this7.renderHighlightedNodes(); | ||
}); | ||
} | ||
WebGLRenderer.prototype.scheduleHighlightedNodesRender = function () { | ||
var _this = this; | ||
if (this.renderHighlightedNodesFrame || this.renderFrame) | ||
return this; | ||
this.renderHighlightedNodesFrame = requestAnimationFrame(function () { | ||
// Resetting state | ||
_this.renderHighlightedNodesFrame = null; | ||
// Rendering | ||
_this.renderHighlightedNodes(); | ||
_this.renderEdgeLabels(); | ||
}); | ||
}; | ||
/** | ||
* Method used to manually refresh. | ||
* | ||
* @return {WebGLRenderer} | ||
*/ | ||
WebGLRenderer.prototype.refresh = function () { | ||
this.needToSoftProcess = true; | ||
this.scheduleRender(); | ||
return this; | ||
}; | ||
/** | ||
* Method used to highlight a node. | ||
@@ -931,17 +779,10 @@ * | ||
*/ | ||
}, { | ||
key: 'highlightNode', | ||
value: function highlightNode(key) { | ||
// TODO: check the existence of the node | ||
// TODO: coerce? | ||
this.highlightedNodes.add(key); | ||
// Rendering | ||
this.scheduleHighlightedNodesRender(); | ||
return this; | ||
} | ||
WebGLRenderer.prototype.highlightNode = function (key) { | ||
// TODO: check the existence of the node | ||
// TODO: coerce? | ||
this.highlightedNodes.add(key); | ||
// Rendering | ||
this.scheduleHighlightedNodesRender(); | ||
return this; | ||
}; | ||
/** | ||
@@ -953,21 +794,56 @@ * Method used to unhighlight a node. | ||
*/ | ||
}, { | ||
key: 'unhighlightNode', | ||
value: function unhighlightNode(key) { | ||
// TODO: check the existence of the node | ||
// TODO: coerce? | ||
this.highlightedNodes.delete(key); | ||
// Rendering | ||
this.scheduleHighlightedNodesRender(); | ||
return this; | ||
} | ||
}]); | ||
return WebGLRenderer; | ||
}(_renderer2.default); | ||
exports.default = WebGLRenderer; | ||
WebGLRenderer.prototype.unhighlightNode = function (key) { | ||
// TODO: check the existence of the node | ||
// TODO: coerce? | ||
this.highlightedNodes.delete(key); | ||
// Rendering | ||
this.scheduleHighlightedNodesRender(); | ||
return this; | ||
}; | ||
/** | ||
* Method used to shut the container & release event listeners. | ||
* | ||
* @return {undefined} | ||
*/ | ||
WebGLRenderer.prototype.kill = function () { | ||
var graph = this.graph; | ||
// Emitting "kill" events so that plugins and such can cleanup | ||
this.emit("kill"); | ||
// Releasing events | ||
this.removeAllListeners(); | ||
// Releasing camera handlers | ||
this.camera.removeListener("updated", this.listeners.camera); | ||
// Releasing DOM events & captors | ||
window.removeEventListener("resize", this.listeners.handleResize); | ||
this.captors.mouse.kill(); | ||
// Releasing graph handlers | ||
graph.removeListener("nodeAdded", this.listeners.addNodeGraphUpdate); | ||
graph.removeListener("nodeDropped", this.listeners.graphUpdate); | ||
graph.removeListener("nodeAttributesUpdated", this.listeners.softGraphUpdate); | ||
graph.removeListener("edgeAdded", this.listeners.addEdgeGraphUpdate); | ||
graph.removeListener("nodeDropped", this.listeners.graphUpdate); | ||
graph.removeListener("edgeAttributesUpdated", this.listeners.softGraphUpdate); | ||
graph.removeListener("cleared", this.listeners.graphUpdate); | ||
// Releasing cache & state | ||
this.quadtree = null; | ||
this.nodeDataCache = null; | ||
this.edgeDataCache = null; | ||
this.highlightedNodes = null; | ||
this.displayedLabels = null; | ||
// Clearing frames | ||
if (this.renderFrame) { | ||
cancelAnimationFrame(this.renderFrame); | ||
this.renderFrame = null; | ||
} | ||
if (this.renderHighlightedNodesFrame) { | ||
cancelAnimationFrame(this.renderHighlightedNodesFrame); | ||
this.renderHighlightedNodesFrame = null; | ||
} | ||
// Destroying canvases | ||
var container = this.container; | ||
while (container.firstChild) | ||
container.removeChild(container.firstChild); | ||
}; | ||
return WebGLRenderer; | ||
}(renderer_1.default)); | ||
exports.default = WebGLRenderer; |
@@ -1,114 +0,85 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _program = require('./program'); | ||
var _program2 = _interopRequireDefault(_program); | ||
var _utils = require('../utils'); | ||
var _edgeFastVert = require('../shaders/edge.fast.vert.glsl'); | ||
var _edgeFastVert2 = _interopRequireDefault(_edgeFastVert); | ||
var _edgeFastFrag = require('../shaders/edge.fast.frag.glsl'); | ||
var _edgeFastFrag2 = _interopRequireDefault(_edgeFastFrag); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Program rendering edges using GL_LINES which is presumably very fast but | ||
* won't render thickness correctly on some GPUs and has some quirks. | ||
*/ | ||
var EdgeFastProgram = function (_Program) { | ||
_inherits(EdgeFastProgram, _Program); | ||
function EdgeFastProgram() { | ||
_classCallCheck(this, EdgeFastProgram); | ||
var _this = _possibleConstructorReturn(this, (EdgeFastProgram.__proto__ || Object.getPrototypeOf(EdgeFastProgram)).call(this)); | ||
_this.vertexShaderSource = _edgeFastVert2.default; | ||
_this.fragmentShaderSource = _edgeFastFrag2.default; | ||
return _this; | ||
} | ||
_createClass(EdgeFastProgram, [{ | ||
key: 'process', | ||
value: function process(array, sourceData, targetData, data, i) { | ||
if (sourceData.hidden || targetData.hidden || data.hidden) { | ||
for (var l = i + EdgeFastProgram.POINTS * EdgeFastProgram.ATTRIBUTES; i < l; i++) { | ||
array[i] = 0; | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js WebGL Renderer Fast Edge Program | ||
* ========================================== | ||
* | ||
* Program rendering edges using GL_LINES which is presumably very fast but | ||
* won't render thickness correctly on some GPUs and has some quirks. | ||
*/ | ||
var program_1 = __importDefault(require("./program")); | ||
var utils_1 = require("../utils"); | ||
var edge_fast_vert_glsl_1 = __importDefault(require("../shaders/edge.fast.vert.glsl.js")); | ||
var edge_fast_frag_glsl_1 = __importDefault(require("../shaders/edge.fast.frag.glsl.js")); | ||
var POINTS = 2, ATTRIBUTES = 3; | ||
var EdgeFastProgram = /** @class */ (function (_super) { | ||
__extends(EdgeFastProgram, _super); | ||
function EdgeFastProgram(gl) { | ||
var _this = _super.call(this, gl, edge_fast_vert_glsl_1.default, edge_fast_frag_glsl_1.default) || this; | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(_this.program, "a_position"); | ||
_this.colorLocation = gl.getAttribLocation(_this.program, "a_color"); | ||
_this.resolutionLocation = gl.getUniformLocation(_this.program, "u_resolution"); | ||
_this.matrixLocation = gl.getUniformLocation(_this.program, "u_matrix"); | ||
// Bindings | ||
gl.enableVertexAttribArray(_this.positionLocation); | ||
gl.enableVertexAttribArray(_this.colorLocation); | ||
gl.vertexAttribPointer(_this.positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(_this.colorLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
return _this; | ||
} | ||
EdgeFastProgram.prototype.allocate = function (capacity) { | ||
this.array = new Float32Array(POINTS * ATTRIBUTES * capacity); | ||
}; | ||
EdgeFastProgram.prototype.process = function (sourceData, targetData, data, offset) { | ||
var array = this.array; | ||
var i = 0; | ||
if (sourceData.hidden || targetData.hidden || data.hidden) { | ||
for (var l = i + POINTS * ATTRIBUTES; i < l; i++) | ||
array[i] = 0; | ||
} | ||
} | ||
var thickness = data.size || 1, | ||
x1 = sourceData.x, | ||
y1 = sourceData.y, | ||
x2 = targetData.x, | ||
y2 = targetData.y, | ||
color = (0, _utils.floatColor)(data.color); | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = color; | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i++] = color; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render(gl, array, params) { | ||
var program = this.program; | ||
gl.useProgram(program); | ||
// Attribute locations | ||
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'); | ||
var buffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | ||
gl.bufferData(gl.ARRAY_BUFFER, array, 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, EdgeFastProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(colorLocation, 1, gl.FLOAT, false, EdgeFastProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
// TODO: use gl line thickness | ||
gl.lineWidth(3); | ||
gl.drawArrays(gl.LINES, 0, array.length / EdgeFastProgram.ATTRIBUTES); | ||
} | ||
}]); | ||
return EdgeFastProgram; | ||
}(_program2.default); | ||
var x1 = sourceData.x, y1 = sourceData.y, x2 = targetData.x, y2 = targetData.y, color = utils_1.floatColor(data.color); | ||
i = POINTS * ATTRIBUTES * offset; | ||
// First point | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = color; | ||
// Second point | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i] = color; | ||
}; | ||
EdgeFastProgram.prototype.bufferData = function () { | ||
var gl = this.gl; | ||
// Vertices data | ||
gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW); | ||
}; | ||
EdgeFastProgram.prototype.render = function (params) { | ||
var gl = this.gl; | ||
var program = this.program; | ||
gl.useProgram(program); | ||
// Binding uniforms | ||
gl.uniform2f(this.resolutionLocation, params.width, params.height); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
// Drawing: | ||
gl.drawArrays(gl.LINES, 0, this.array.length / ATTRIBUTES); | ||
}; | ||
return EdgeFastProgram; | ||
}(program_1.default)); | ||
exports.default = EdgeFastProgram; | ||
EdgeFastProgram.POINTS = 2; | ||
EdgeFastProgram.ATTRIBUTES = 3; |
@@ -1,198 +0,167 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _program = require('./program'); | ||
var _program2 = _interopRequireDefault(_program); | ||
var _utils = require('../utils'); | ||
var _edgeVert = require('../shaders/edge.vert.glsl'); | ||
var _edgeVert2 = _interopRequireDefault(_edgeVert); | ||
var _edgeFrag = require('../shaders/edge.frag.glsl'); | ||
var _edgeFrag2 = _interopRequireDefault(_edgeFrag); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Program rendering edges as thick lines using four points translated | ||
* orthogonally from the source & target's centers by half thickness. | ||
* | ||
* Rendering two triangles by using only four points is made possible through | ||
* the use of indices. | ||
* | ||
* This method should be faster than the 6 points / 2 triangles approach and | ||
* should handle thickness better than with gl.LINES. | ||
* | ||
* This version of the shader balances geometry computation evenly between | ||
* the CPU & GPU (normals are computed on the CPU side). | ||
*/ | ||
var EdgeProgram = function (_Program) { | ||
_inherits(EdgeProgram, _Program); | ||
function EdgeProgram(gl) { | ||
_classCallCheck(this, EdgeProgram); | ||
// Initializing buffers | ||
var _this = _possibleConstructorReturn(this, (EdgeProgram.__proto__ || Object.getPrototypeOf(EdgeProgram)).call(this, gl, _edgeVert2.default, _edgeFrag2.default)); | ||
_this.buffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, _this.buffer); | ||
_this.indicesBuffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _this.indicesBuffer); | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(_this.program, 'a_position'); | ||
_this.normalLocation = gl.getAttribLocation(_this.program, 'a_normal'); | ||
_this.thicknessLocation = gl.getAttribLocation(_this.program, 'a_thickness'); | ||
_this.colorLocation = gl.getAttribLocation(_this.program, 'a_color'); | ||
_this.resolutionLocation = gl.getUniformLocation(_this.program, 'u_resolution'); | ||
_this.ratioLocation = gl.getUniformLocation(_this.program, 'u_ratio'); | ||
_this.matrixLocation = gl.getUniformLocation(_this.program, 'u_matrix'); | ||
// Bindings | ||
gl.enableVertexAttribArray(_this.positionLocation); | ||
gl.enableVertexAttribArray(_this.normalLocation); | ||
gl.enableVertexAttribArray(_this.thicknessLocation); | ||
gl.enableVertexAttribArray(_this.colorLocation); | ||
gl.vertexAttribPointer(_this.positionLocation, 2, gl.FLOAT, false, EdgeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(_this.normalLocation, 2, gl.FLOAT, false, EdgeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(_this.thicknessLocation, 1, gl.FLOAT, false, EdgeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16); | ||
gl.vertexAttribPointer(_this.colorLocation, 1, gl.FLOAT, false, EdgeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 20); | ||
return _this; | ||
} | ||
_createClass(EdgeProgram, [{ | ||
key: 'process', | ||
value: function process(array, sourceData, targetData, data, i) { | ||
if (sourceData.hidden || targetData.hidden || data.hidden) { | ||
for (var l = i + EdgeProgram.POINTS * EdgeProgram.ATTRIBUTES; i < l; i++) { | ||
array[i] = 0; | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js WebGL Renderer Edge Program | ||
* ===================================== | ||
* | ||
* Program rendering edges as thick lines using four points translated | ||
* orthogonally from the source & target's centers by half thickness. | ||
* | ||
* Rendering two triangles by using only four points is made possible through | ||
* the use of indices. | ||
* | ||
* This method should be faster than the 6 points / 2 triangles approach and | ||
* should handle thickness better than with gl.LINES. | ||
* | ||
* This version of the shader balances geometry computation evenly between | ||
* the CPU & GPU (normals are computed on the CPU side). | ||
*/ | ||
var program_1 = __importDefault(require("./program")); | ||
var utils_1 = require("../utils"); | ||
var edge_vert_glsl_1 = __importDefault(require("../shaders/edge.vert.glsl.js")); | ||
var edge_frag_glsl_1 = __importDefault(require("../shaders/edge.frag.glsl.js")); | ||
var POINTS = 4, ATTRIBUTES = 6, STRIDE = POINTS * ATTRIBUTES; | ||
var EdgeProgram = /** @class */ (function (_super) { | ||
__extends(EdgeProgram, _super); | ||
function EdgeProgram(gl) { | ||
var _this = _super.call(this, gl, edge_vert_glsl_1.default, edge_frag_glsl_1.default) || this; | ||
// Initializing indices buffer | ||
_this.indicesBuffer = gl.createBuffer(); | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(_this.program, "a_position"); | ||
_this.normalLocation = gl.getAttribLocation(_this.program, "a_normal"); | ||
_this.thicknessLocation = gl.getAttribLocation(_this.program, "a_thickness"); | ||
_this.colorLocation = gl.getAttribLocation(_this.program, "a_color"); | ||
_this.resolutionLocation = gl.getUniformLocation(_this.program, "u_resolution"); | ||
_this.ratioLocation = gl.getUniformLocation(_this.program, "u_ratio"); | ||
_this.matrixLocation = gl.getUniformLocation(_this.program, "u_matrix"); | ||
_this.scaleLocation = gl.getUniformLocation(_this.program, "u_scale"); | ||
_this.bind(); | ||
// Enabling the OES_element_index_uint extension | ||
// NOTE: on older GPUs, this means that really large graphs won't | ||
// have all their edges rendered. But it seems that the | ||
// `OES_element_index_uint` is quite everywhere so we'll handle | ||
// the potential issue if it really arises. | ||
// NOTE: when using webgl2, the extension is enabled by default | ||
_this.canUse32BitsIndices = utils_1.canUse32BitsIndices(gl); | ||
_this.IndicesArray = _this.canUse32BitsIndices ? Uint32Array : Uint16Array; | ||
_this.indicesType = _this.canUse32BitsIndices ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT; | ||
return _this; | ||
} | ||
EdgeProgram.prototype.bind = function () { | ||
var gl = this.gl; | ||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer); | ||
// Bindings | ||
gl.enableVertexAttribArray(this.positionLocation); | ||
gl.enableVertexAttribArray(this.normalLocation); | ||
gl.enableVertexAttribArray(this.thicknessLocation); | ||
gl.enableVertexAttribArray(this.colorLocation); | ||
gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(this.normalLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(this.thicknessLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16); | ||
gl.vertexAttribPointer(this.colorLocation, 4, gl.UNSIGNED_BYTE, true, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 20); | ||
}; | ||
EdgeProgram.prototype.allocate = function (capacity) { | ||
this.array = new Float32Array(POINTS * ATTRIBUTES * capacity); | ||
}; | ||
EdgeProgram.prototype.process = function (sourceData, targetData, data, offset) { | ||
if (sourceData.hidden || targetData.hidden || data.hidden) { | ||
for (var i_1 = offset * STRIDE, l = i_1 + STRIDE; i_1 < l; i_1++) | ||
this.array[i_1] = 0; | ||
return; | ||
} | ||
} | ||
var thickness = data.size || 1, | ||
x1 = sourceData.x, | ||
y1 = sourceData.y, | ||
x2 = targetData.x, | ||
y2 = targetData.y, | ||
color = (0, _utils.floatColor)(data.color); | ||
// Computing normals | ||
var dx = x2 - x1, | ||
dy = y2 - y1; | ||
var len = dx * dx + dy * dy, | ||
n1 = 0, | ||
n2 = 0; | ||
if (len) { | ||
len = 1 / Math.sqrt(len); | ||
n1 = -dy * len; | ||
n2 = dx * len; | ||
} | ||
// First point | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = n1; | ||
array[i++] = n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// First point flipped | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = -n1; | ||
array[i++] = -n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// Second point | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i++] = n1; | ||
array[i++] = n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// Second point flipped | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i++] = -n1; | ||
array[i++] = -n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
} | ||
}, { | ||
key: 'computeIndices', | ||
value: function computeIndices(array) { | ||
var l = array.length / EdgeProgram.ATTRIBUTES; | ||
var size = l + l / 2; | ||
var indices = new Uint16Array(size); | ||
for (var i = 0, c = 0; i < size; i += 4) { | ||
indices[c++] = i; | ||
indices[c++] = i + 1; | ||
indices[c++] = i + 2; | ||
indices[c++] = i + 2; | ||
indices[c++] = i + 1; | ||
indices[c++] = i + 3; | ||
} | ||
return indices; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render(gl, array, params) { | ||
var program = this.program; | ||
gl.useProgram(program); | ||
// Buffer data | ||
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); | ||
// Binding uniforms | ||
gl.uniform2f(this.resolutionLocation, params.width, params.height); | ||
gl.uniform1f(this.ratioLocation, params.ratio / Math.pow(params.ratio, params.edgesPowRatio)); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
// Buffering indices data | ||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, params.indices, gl.STATIC_DRAW); | ||
// Drawing: | ||
gl.drawElements(gl.TRIANGLES, params.indices.length, gl.UNSIGNED_SHORT, 0); | ||
} | ||
}]); | ||
return EdgeProgram; | ||
}(_program2.default); | ||
var thickness = data.size || 1, x1 = sourceData.x, y1 = sourceData.y, x2 = targetData.x, y2 = targetData.y, color = utils_1.floatColor(data.color); | ||
// Computing normals | ||
var dx = x2 - x1, dy = y2 - y1; | ||
var len = dx * dx + dy * dy, n1 = 0, n2 = 0; | ||
if (len) { | ||
len = 1 / Math.sqrt(len); | ||
n1 = -dy * len; | ||
n2 = dx * len; | ||
} | ||
var i = POINTS * ATTRIBUTES * offset; | ||
var array = this.array; | ||
// First point | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = n1; | ||
array[i++] = n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// First point flipped | ||
array[i++] = x1; | ||
array[i++] = y1; | ||
array[i++] = -n1; | ||
array[i++] = -n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// Second point | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i++] = n1; | ||
array[i++] = n2; | ||
array[i++] = thickness; | ||
array[i++] = color; | ||
// Second point flipped | ||
array[i++] = x2; | ||
array[i++] = y2; | ||
array[i++] = -n1; | ||
array[i++] = -n2; | ||
array[i++] = thickness; | ||
array[i] = color; | ||
}; | ||
EdgeProgram.prototype.computeIndices = function () { | ||
var l = this.array.length / ATTRIBUTES; | ||
var size = l + l / 2; | ||
var indices = new this.IndicesArray(size); | ||
for (var i = 0, c = 0; i < l; i += 4) { | ||
indices[c++] = i; | ||
indices[c++] = i + 1; | ||
indices[c++] = i + 2; | ||
indices[c++] = i + 2; | ||
indices[c++] = i + 1; | ||
indices[c++] = i + 3; | ||
} | ||
this.indicesArray = indices; | ||
}; | ||
EdgeProgram.prototype.bufferData = function () { | ||
var gl = this.gl; | ||
// Vertices data | ||
gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW); | ||
// Indices data | ||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indicesArray, gl.STATIC_DRAW); | ||
}; | ||
EdgeProgram.prototype.render = function (params) { | ||
var gl = this.gl; | ||
var program = this.program; | ||
gl.useProgram(program); | ||
// Binding uniforms | ||
// TODO: precise the uniform names | ||
gl.uniform2f(this.resolutionLocation, params.width, params.height); | ||
gl.uniform1f(this.ratioLocation, | ||
// 1 / Math.pow(params.ratio, params.edgesPowRatio) | ||
params.ratio); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
gl.uniform1f(this.scaleLocation, params.scalingRatio); | ||
// Drawing: | ||
gl.drawElements(gl.TRIANGLES, this.indicesArray.length, this.indicesType, 0); | ||
}; | ||
return EdgeProgram; | ||
}(program_1.default)); | ||
exports.default = EdgeProgram; | ||
EdgeProgram.POINTS = 4; | ||
EdgeProgram.ATTRIBUTES = 6; |
@@ -1,105 +0,87 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _program = require('./program'); | ||
var _program2 = _interopRequireDefault(_program); | ||
var _utils = require('../utils'); | ||
var _nodeFastVert = require('../shaders/node.fast.vert.glsl'); | ||
var _nodeFastVert2 = _interopRequireDefault(_nodeFastVert); | ||
var _nodeFastFrag = require('../shaders/node.fast.frag.glsl'); | ||
var _nodeFastFrag2 = _interopRequireDefault(_nodeFastFrag); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Simple program rendering nodes using GL_POINTS. This is faster than the | ||
* three triangle option but has some quirks and is not supported equally by | ||
* every GPU. | ||
*/ | ||
var NodeProgramFast = function (_Program) { | ||
_inherits(NodeProgramFast, _Program); | ||
function NodeProgramFast() { | ||
_classCallCheck(this, NodeProgramFast); | ||
var _this = _possibleConstructorReturn(this, (NodeProgramFast.__proto__ || Object.getPrototypeOf(NodeProgramFast)).call(this)); | ||
_this.vertexShaderSource = _nodeFastVert2.default; | ||
_this.fragmentShaderSource = _nodeFastFrag2.default; | ||
return _this; | ||
} | ||
_createClass(NodeProgramFast, [{ | ||
key: 'process', | ||
value: function process(array, data, i) { | ||
var color = (0, _utils.floatColor)(data.color); | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Simple program rendering nodes using GL_POINTS. This is faster than the | ||
* three triangle option but has some quirks and is not supported equally by | ||
* every GPU. | ||
*/ | ||
var program_1 = __importDefault(require("./program")); | ||
var utils_1 = require("../utils"); | ||
var node_fast_vert_glsl_1 = __importDefault(require("../shaders/node.fast.vert.glsl.js")); | ||
var node_fast_frag_glsl_1 = __importDefault(require("../shaders/node.fast.frag.glsl.js")); | ||
var POINTS = 1, ATTRIBUTES = 4; | ||
var NodeProgramFast = /** @class */ (function (_super) { | ||
__extends(NodeProgramFast, _super); | ||
function NodeProgramFast(gl) { | ||
var _this = _super.call(this, gl, node_fast_vert_glsl_1.default, node_fast_frag_glsl_1.default) || this; | ||
var program = _this.program; | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(program, "a_position"); | ||
_this.sizeLocation = gl.getAttribLocation(program, "a_size"); | ||
_this.colorLocation = gl.getAttribLocation(program, "a_color"); | ||
_this.matrixLocation = gl.getUniformLocation(program, "u_matrix"); | ||
_this.ratioLocation = gl.getUniformLocation(program, "u_ratio"); | ||
_this.scaleLocation = gl.getUniformLocation(program, "u_scale"); | ||
// Bindings | ||
gl.enableVertexAttribArray(_this.positionLocation); | ||
gl.enableVertexAttribArray(_this.sizeLocation); | ||
gl.enableVertexAttribArray(_this.colorLocation); | ||
gl.vertexAttribPointer(_this.positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(_this.sizeLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(_this.colorLocation, 4, gl.UNSIGNED_BYTE, true, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 12); | ||
return _this; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render(gl, array, params) { | ||
var program = this.program; | ||
gl.useProgram(program); | ||
// Attribute locations | ||
var positionLocation = gl.getAttribLocation(program, 'a_position'), | ||
sizeLocation = gl.getAttribLocation(program, 'a_size'), | ||
colorLocation = gl.getAttribLocation(program, 'a_color'), | ||
resolutionLocation = gl.getUniformLocation(program, 'u_resolution'), | ||
matrixLocation = gl.getUniformLocation(program, 'u_matrix'), | ||
ratioLocation = gl.getUniformLocation(program, 'u_ratio'), | ||
scaleLocation = gl.getUniformLocation(program, 'u_scale'); | ||
var buffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | ||
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); | ||
gl.uniform2f(resolutionLocation, params.width, params.height); | ||
gl.uniform1f(ratioLocation, 1 / Math.pow(params.ratio, params.nodesPowRatio)); | ||
gl.uniform1f(scaleLocation, params.scalingRatio); | ||
gl.uniformMatrix3fv(matrixLocation, false, params.matrix); | ||
gl.enableVertexAttribArray(positionLocation); | ||
gl.enableVertexAttribArray(sizeLocation); | ||
gl.enableVertexAttribArray(colorLocation); | ||
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, NodeProgramFast.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(sizeLocation, 1, gl.FLOAT, false, NodeProgramFast.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(colorLocation, 1, gl.FLOAT, false, NodeProgramFast.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 12); | ||
gl.drawArrays(gl.POINTS, 0, array.length / NodeProgramFast.ATTRIBUTES); | ||
} | ||
}]); | ||
return NodeProgramFast; | ||
}(_program2.default); | ||
NodeProgramFast.prototype.allocate = function (capacity) { | ||
this.array = new Float32Array(POINTS * ATTRIBUTES * capacity); | ||
}; | ||
NodeProgramFast.prototype.process = function (data, offset) { | ||
var color = utils_1.floatColor(data.color); | ||
var i = offset * POINTS * ATTRIBUTES; | ||
var array = this.array; | ||
if (data.hidden) { | ||
array[i++] = 0; | ||
array[i++] = 0; | ||
array[i++] = 0; | ||
array[i++] = 0; | ||
return; | ||
} | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i] = color; | ||
}; | ||
NodeProgramFast.prototype.bufferData = function () { | ||
var gl = this.gl; | ||
gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW); | ||
}; | ||
NodeProgramFast.prototype.render = function (params) { | ||
var gl = this.gl; | ||
var program = this.program; | ||
gl.useProgram(program); | ||
gl.uniform1f(this.ratioLocation, 1 / Math.pow(params.ratio, params.nodesPowRatio)); | ||
gl.uniform1f(this.scaleLocation, params.scalingRatio); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
gl.drawArrays(gl.POINTS, 0, this.array.length / ATTRIBUTES); | ||
}; | ||
return NodeProgramFast; | ||
}(program_1.default)); | ||
exports.default = NodeProgramFast; | ||
NodeProgramFast.POINTS = 1; | ||
NodeProgramFast.ATTRIBUTES = 4; |
@@ -1,129 +0,97 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _program = require('./program'); | ||
var _program2 = _interopRequireDefault(_program); | ||
var _utils = require('../utils'); | ||
var _nodeVert = require('../shaders/node.vert.glsl'); | ||
var _nodeVert2 = _interopRequireDefault(_nodeVert); | ||
var _nodeFrag = require('../shaders/node.frag.glsl'); | ||
var _nodeFrag2 = _interopRequireDefault(_nodeFrag); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Simple program rendering nodes as discs, shaped by triangles using the | ||
* `gl.TRIANGLES` display mode. So, to draw one node, it will need to store | ||
* three times the center of the node, with the color, the size and an angle | ||
* indicating which "corner" of the triangle to draw. | ||
*/ | ||
var ANGLE_1 = 0, | ||
ANGLE_2 = 2 * Math.PI / 3, | ||
ANGLE_3 = 4 * Math.PI / 3; | ||
var NodeProgram = function (_Program) { | ||
_inherits(NodeProgram, _Program); | ||
function NodeProgram(gl) { | ||
_classCallCheck(this, NodeProgram); | ||
// Initializing buffers | ||
var _this = _possibleConstructorReturn(this, (NodeProgram.__proto__ || Object.getPrototypeOf(NodeProgram)).call(this, gl, _nodeVert2.default, _nodeFrag2.default)); | ||
_this.buffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, _this.buffer); | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(_this.program, 'a_position'); | ||
_this.sizeLocation = gl.getAttribLocation(_this.program, 'a_size'); | ||
_this.colorLocation = gl.getAttribLocation(_this.program, 'a_color'); | ||
_this.angleLocation = gl.getAttribLocation(_this.program, 'a_angle'); | ||
_this.resolutionLocation = gl.getUniformLocation(_this.program, 'u_resolution'); | ||
_this.matrixLocation = gl.getUniformLocation(_this.program, 'u_matrix'); | ||
_this.ratioLocation = gl.getUniformLocation(_this.program, 'u_ratio'); | ||
_this.scaleLocation = gl.getUniformLocation(_this.program, 'u_scale'); | ||
// Bindings | ||
gl.enableVertexAttribArray(_this.positionLocation); | ||
gl.enableVertexAttribArray(_this.sizeLocation); | ||
gl.enableVertexAttribArray(_this.colorLocation); | ||
gl.enableVertexAttribArray(_this.angleLocation); | ||
gl.vertexAttribPointer(_this.positionLocation, 2, gl.FLOAT, false, NodeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(_this.sizeLocation, 1, gl.FLOAT, false, NodeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(_this.colorLocation, 1, gl.FLOAT, false, NodeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 12); | ||
gl.vertexAttribPointer(_this.angleLocation, 1, gl.FLOAT, false, NodeProgram.ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16); | ||
return _this; | ||
} | ||
_createClass(NodeProgram, [{ | ||
key: 'process', | ||
value: function process(array, data, i) { | ||
var color = (0, _utils.floatColor)(data.color); | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i++] = ANGLE_1; | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i++] = ANGLE_2; | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i++] = ANGLE_3; | ||
"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 (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Sigma.js WebGL Renderer Node Program | ||
* ===================================== | ||
* | ||
* Simple program rendering nodes as discs, shaped by triangles using the | ||
* `gl.TRIANGLES` display mode. So, to draw one node, it will need to store | ||
* three times the center of the node, with the color, the size and an angle | ||
* indicating which "corner" of the triangle to draw. | ||
*/ | ||
var program_1 = __importDefault(require("./program")); | ||
var utils_1 = require("../utils"); | ||
var node_vert_glsl_1 = __importDefault(require("../shaders/node.vert.glsl.js")); | ||
var node_frag_glsl_1 = __importDefault(require("../shaders/node.frag.glsl.js")); | ||
var ANGLE_1 = 0, ANGLE_2 = (2 * Math.PI) / 3, ANGLE_3 = (4 * Math.PI) / 3; | ||
var POINTS = 3, ATTRIBUTES = 5; | ||
var NodeProgram = /** @class */ (function (_super) { | ||
__extends(NodeProgram, _super); | ||
function NodeProgram(gl) { | ||
var _this = _super.call(this, gl, node_vert_glsl_1.default, node_frag_glsl_1.default) || this; | ||
// Locations | ||
_this.positionLocation = gl.getAttribLocation(_this.program, "a_position"); | ||
_this.sizeLocation = gl.getAttribLocation(_this.program, "a_size"); | ||
_this.colorLocation = gl.getAttribLocation(_this.program, "a_color"); | ||
_this.angleLocation = gl.getAttribLocation(_this.program, "a_angle"); | ||
_this.resolutionLocation = gl.getUniformLocation(_this.program, "u_resolution"); | ||
_this.matrixLocation = gl.getUniformLocation(_this.program, "u_matrix"); | ||
_this.ratioLocation = gl.getUniformLocation(_this.program, "u_ratio"); | ||
_this.scaleLocation = gl.getUniformLocation(_this.program, "u_scale"); | ||
// Bindings | ||
gl.enableVertexAttribArray(_this.positionLocation); | ||
gl.enableVertexAttribArray(_this.sizeLocation); | ||
gl.enableVertexAttribArray(_this.colorLocation); | ||
gl.enableVertexAttribArray(_this.angleLocation); | ||
gl.vertexAttribPointer(_this.positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); | ||
gl.vertexAttribPointer(_this.sizeLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); | ||
gl.vertexAttribPointer(_this.colorLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 12); | ||
gl.vertexAttribPointer(_this.angleLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16); | ||
return _this; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render(gl, array, params) { | ||
var program = this.program; | ||
gl.useProgram(program); | ||
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); | ||
gl.uniform2f(this.resolutionLocation, params.width, params.height); | ||
gl.uniform1f(this.ratioLocation, 1 / Math.pow(params.ratio, params.nodesPowRatio)); | ||
gl.uniform1f(this.scaleLocation, params.scalingRatio); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
gl.drawArrays(gl.TRIANGLES, 0, array.length / NodeProgram.ATTRIBUTES); | ||
} | ||
}]); | ||
return NodeProgram; | ||
}(_program2.default); | ||
NodeProgram.prototype.allocate = function (capacity) { | ||
this.array = new Float32Array(POINTS * ATTRIBUTES * capacity); | ||
}; | ||
NodeProgram.prototype.process = function (data, offset) { | ||
var color = utils_1.floatColor(data.color); | ||
var i = offset * POINTS * ATTRIBUTES; | ||
var array = this.array; | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i++] = ANGLE_1; | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i++] = ANGLE_2; | ||
array[i++] = data.x; | ||
array[i++] = data.y; | ||
array[i++] = data.size; | ||
array[i++] = color; | ||
array[i] = ANGLE_3; | ||
}; | ||
NodeProgram.prototype.bufferData = function () { | ||
var gl = this.gl; | ||
gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW); | ||
}; | ||
NodeProgram.prototype.render = function (params) { | ||
var gl = this.gl; | ||
var program = this.program; | ||
gl.useProgram(program); | ||
gl.uniform2f(this.resolutionLocation, params.width, params.height); | ||
gl.uniform1f(this.ratioLocation, 1 / Math.pow(params.ratio, params.nodesPowRatio)); | ||
gl.uniform1f(this.scaleLocation, params.scalingRatio); | ||
gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix); | ||
gl.drawArrays(gl.TRIANGLES, 0, this.array.length / ATTRIBUTES); | ||
}; | ||
return NodeProgram; | ||
}(program_1.default)); | ||
exports.default = NodeProgram; | ||
NodeProgram.POINTS = 3; | ||
NodeProgram.ATTRIBUTES = 5; |
@@ -1,20 +0,32 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** | ||
* Sigma.js WebGL Renderer Program | ||
* ================================ | ||
* | ||
* Class representing a single WebGL program used by sigma's WebGL renderer. | ||
*/ | ||
var _utils = require('../shaders/utils'); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
"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 __spread = (this && this.__spread) || function () { | ||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createCompoundProgram = void 0; | ||
/** | ||
* Sigma.js WebGL Renderer Program | ||
* ================================ | ||
* | ||
* Class representing a single WebGL program used by sigma's WebGL renderer. | ||
*/ | ||
var utils_1 = require("../shaders/utils"); | ||
/** | ||
* Program class. | ||
@@ -24,35 +36,69 @@ * | ||
*/ | ||
var Program = function () { | ||
function Program(gl, vertexShaderSource, fragmentShaderSource) { | ||
_classCallCheck(this, Program); | ||
this.vertexShaderSource = vertexShaderSource; | ||
this.fragmentShaderSource = fragmentShaderSource; | ||
this.load(gl); | ||
} | ||
/** | ||
* Method used to load the program into a webgl context. | ||
* | ||
* @param {WebGLContext} gl - The WebGL context. | ||
* @return {WebGLProgram} | ||
*/ | ||
_createClass(Program, [{ | ||
key: 'load', | ||
value: function load(gl) { | ||
this.vertexShader = (0, _utils.loadVertexShader)(gl, this.vertexShaderSource); | ||
this.fragmentShader = (0, _utils.loadFragmentShader)(gl, this.fragmentShaderSource); | ||
this.program = (0, _utils.loadProgram)(gl, [this.vertexShader, this.fragmentShader]); | ||
return this.program; | ||
var Program = /** @class */ (function () { | ||
function Program(gl, vertexShaderSource, fragmentShaderSource) { | ||
this.array = null; | ||
// Binding context | ||
this.gl = gl; | ||
this.vertexShaderSource = vertexShaderSource; | ||
this.fragmentShaderSource = fragmentShaderSource; | ||
this.buffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); | ||
this.load(gl); | ||
} | ||
}]); | ||
return Program; | ||
}(); | ||
exports.default = Program; | ||
/** | ||
* Method used to load the program into a webgl context. | ||
* | ||
* @param {WebGLContext} gl - The WebGL context. | ||
* @return {WebGLProgram} | ||
*/ | ||
Program.prototype.load = function (gl) { | ||
this.vertexShader = utils_1.loadVertexShader(gl, this.vertexShaderSource); | ||
this.fragmentShader = utils_1.loadFragmentShader(gl, this.fragmentShaderSource); | ||
this.program = utils_1.loadProgram(gl, [this.vertexShader, this.fragmentShader]); | ||
return this.program; | ||
}; | ||
return Program; | ||
}()); | ||
exports.default = Program; | ||
/** | ||
* Helper function combining two or more programs into a single compound one. | ||
* Note that this is more a quick & easy way to combine program than a really | ||
* performant option. More performant programs can be written entirely. | ||
* | ||
* @param {array} programClasses - Program classes to combine. | ||
* @return {function} | ||
*/ | ||
// TODO: maybe those should handle their own canvases | ||
function createCompoundProgram(programClasses) { | ||
return /** @class */ (function () { | ||
function CompoundProgram(gl) { | ||
this.programs = programClasses.map(function (ProgramClass) { return new ProgramClass(gl); }); | ||
} | ||
CompoundProgram.prototype.allocate = function (capacity) { | ||
this.programs.forEach(function (program) { return program.allocate(capacity); }); | ||
}; | ||
CompoundProgram.prototype.process = function () { | ||
var args = arguments; | ||
this.programs.forEach(function (program) { return program.process.apply(program, __spread(args)); }); | ||
}; | ||
CompoundProgram.prototype.computeIndices = function () { | ||
this.programs.forEach(function (program) { | ||
if (typeof program.computeIndices === "function") | ||
program.computeIndices(); | ||
}); | ||
}; | ||
CompoundProgram.prototype.bufferData = function () { | ||
this.programs.forEach(function (program) { return program.bufferData(); }); | ||
}; | ||
CompoundProgram.prototype.render = function () { | ||
var args = arguments; | ||
this.programs.forEach(function (program) { | ||
program.bind(); | ||
program.bufferData(); | ||
program.render.apply(program, __spread(args)); | ||
}); | ||
}; | ||
return CompoundProgram; | ||
}()); | ||
} | ||
exports.createCompoundProgram = createCompoundProgram; |
@@ -1,7 +0,2 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.loadProgram = loadProgram; | ||
"use strict"; | ||
/** | ||
@@ -13,3 +8,4 @@ * Sigma.js Shader Utils | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadProgram = exports.loadFragmentShader = exports.loadVertexShader = void 0; | ||
/** | ||
@@ -19,58 +15,40 @@ * Function used to load a shader. | ||
function loadShader(type, gl, source) { | ||
var glType = type === 'VERTEX' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER; | ||
// Creating the shader | ||
var shader = gl.createShader(glType); | ||
// Loading source | ||
gl.shaderSource(shader, source); | ||
// Compiling the shader | ||
gl.compileShader(shader); | ||
// Retrieving compilation status | ||
var successfullyCompiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); | ||
// Throwing if something went awry | ||
if (!successfullyCompiled) { | ||
var infoLog = gl.getShaderInfoLog(shader); | ||
gl.deleteShader(shader); | ||
throw new Error('sigma/renderers/weblg/shaders/utils.loadShader: error while compiling the shader:\n' + infoLog + '\n' + source); | ||
} | ||
return shader; | ||
var glType = type === "VERTEX" ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER; | ||
// Creating the shader | ||
var shader = gl.createShader(glType); | ||
// Loading source | ||
gl.shaderSource(shader, source); | ||
// Compiling the shader | ||
gl.compileShader(shader); | ||
// Retrieving compilation status | ||
var successfullyCompiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); | ||
// Throwing if something went awry | ||
if (!successfullyCompiled) { | ||
var infoLog = gl.getShaderInfoLog(shader); | ||
gl.deleteShader(shader); | ||
throw new Error("sigma/renderers/webgl/shaders/utils.loadShader: error while compiling the shader:\n" + infoLog + "\n" + source); | ||
} | ||
return shader; | ||
} | ||
var loadVertexShader = loadShader.bind(null, 'VERTEX'), | ||
loadFragmentShader = loadShader.bind(null, 'FRAGMENT'); | ||
var loadVertexShader = loadShader.bind(null, "VERTEX"), loadFragmentShader = loadShader.bind(null, "FRAGMENT"); | ||
exports.loadVertexShader = loadVertexShader; | ||
exports.loadFragmentShader = loadFragmentShader; | ||
/** | ||
* Function used to load a program. | ||
*/ | ||
function loadProgram(gl, shaders) { | ||
var program = gl.createProgram(); | ||
var i = void 0, | ||
l = void 0; | ||
// Attaching the shaders | ||
for (i = 0, l = shaders.length; i < l; i++) { | ||
gl.attachShader(program, shaders[i]); | ||
}gl.linkProgram(program); | ||
// Checking status | ||
var successfullyLinked = gl.getProgramParameter(program, gl.LINK_STATUS); | ||
if (!successfullyLinked) { | ||
gl.deleteProgram(program); | ||
throw new Error('sigma/renderers/weblg/shaders/utils.loadProgram: error while linking the program.'); | ||
} | ||
return program; | ||
} | ||
var program = gl.createProgram(); | ||
var i, l; | ||
// Attaching the shaders | ||
for (i = 0, l = shaders.length; i < l; i++) | ||
gl.attachShader(program, shaders[i]); | ||
gl.linkProgram(program); | ||
// Checking status | ||
var successfullyLinked = gl.getProgramParameter(program, gl.LINK_STATUS); | ||
if (!successfullyLinked) { | ||
gl.deleteProgram(program); | ||
throw new Error("sigma/renderers/webgl/shaders/utils.loadProgram: error while linking the program."); | ||
} | ||
return program; | ||
} | ||
exports.loadProgram = loadProgram; |
@@ -1,90 +0,74 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.floatColor = floatColor; | ||
exports.matrixFromCamera = matrixFromCamera; | ||
exports.extractPixel = extractPixel; | ||
var _glMatrix = require('gl-matrix'); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.canUse32BitsIndices = exports.extractPixel = exports.matrixFromCamera = exports.floatColor = void 0; | ||
/** | ||
* Sigma.js WebGL Renderer Utils | ||
* ============================== | ||
* | ||
* Miscelleanous helper functions used by sigma's WebGL renderer. | ||
*/ | ||
var matrices_1 = require("./matrices"); | ||
/** | ||
* Memoized function returning a float-encoded color from various string | ||
* formats describing colors. | ||
*/ | ||
var FLOAT_COLOR_CACHE = {}; /** | ||
* Sigma.js WebGL Renderer Utils | ||
* ============================== | ||
* | ||
* Miscelleanous helper functions used by sigma's WebGL renderer. | ||
*/ | ||
// TODO: this is heavy for what we do with it | ||
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*$/; | ||
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; | ||
// 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); | ||
// 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); | ||
r = +match[1]; | ||
g = +match[2]; | ||
b = +match[3]; | ||
// Handling rgb notation | ||
else if (RGBA_TEST_REGEX.test(val)) { | ||
var match = val.match(RGBA_EXTRACT_REGEX); | ||
r = +match[1]; | ||
g = +match[2]; | ||
b = +match[3]; | ||
if (match[4]) | ||
a = +match[4]; | ||
} | ||
var color = r * 256 * 256 + g * 256 + b; | ||
FLOAT_COLOR_CACHE[val] = color; | ||
return color; | ||
a = (a * 255) | 0; | ||
var bits = ((a << 24) | (b << 16) | (g << 8) | r) & 0xfeffffff; | ||
INT32[0] = bits; | ||
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) { | ||
var angle = state.angle, | ||
ratio = state.ratio, | ||
x = state.x, | ||
y = state.y; | ||
var matrix = _glMatrix.mat3.create(), | ||
scale = _glMatrix.mat3.fromScaling(_glMatrix.mat3.create(), [1 / ratio, 1 / ratio]), | ||
rotation = _glMatrix.mat3.fromRotation(_glMatrix.mat3.create(), -angle), | ||
translation = _glMatrix.mat3.fromTranslation(_glMatrix.mat3.create(), [-x, -y]); | ||
_glMatrix.mat3.multiply(matrix, scale, rotation); | ||
_glMatrix.mat3.multiply(matrix, matrix, translation); | ||
return matrix; | ||
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; | ||
/** | ||
@@ -94,7 +78,14 @@ * 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; | ||
} | ||
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; |
55
utils.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.assign = assign; | ||
/** | ||
@@ -13,6 +8,17 @@ * Sigma.js Utils | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.assign = exports.isPlainObject = void 0; | ||
/** | ||
* Very simple Object.assign-like function. | ||
* Checks whether the given value is a plain object. | ||
* | ||
* @param {mixed} value - Target value. | ||
* @return {boolean} | ||
*/ | ||
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. | ||
@@ -23,17 +29,22 @@ * @param {object} [...objects] - Objects to merge. | ||
function assign(target) { | ||
target = target || {}; | ||
for (var _len = arguments.length, objects = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
objects[_key - 1] = arguments[_key]; | ||
} | ||
for (var i = 0, l = objects.length; i < l; i++) { | ||
if (!objects[i]) continue; | ||
for (var k in objects[i]) { | ||
target[k] = objects[i][k]; | ||
var objects = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
objects[_i - 1] = arguments[_i]; | ||
} | ||
} | ||
return target; | ||
} | ||
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] = assign(target[k], o[k]); | ||
} | ||
else { | ||
target[k] = o[k]; | ||
} | ||
} | ||
} | ||
return target; | ||
} | ||
exports.assign = assign; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
680009
202
13034
35
4
32
1
+ Addedgraphology-metrics@1.13.0
+ Added@yomguithereal/helpers@1.1.1(transitive)
+ Addedevents@3.3.0(transitive)
+ Addedgraphology-indices@0.16.6(transitive)
+ Addedgraphology-metrics@1.13.0(transitive)
+ Addedgraphology-shortest-path@1.5.2(transitive)
+ Addedgraphology-types@0.16.0(transitive)
+ Addedgraphology-utils@2.5.2(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmnemonist@0.34.00.38.50.39.8(transitive)
+ Addedobliterator@1.6.12.0.5(transitive)
- Removedgl-matrix@^2.3.2
- Removedevents@1.1.1(transitive)
- Removedgl-matrix@2.8.1(transitive)
Updatedevents@^3.0.0
Updatedgraphology-utils@^1.4.0