|
|
- ;(function(undefined) {
- 'use strict';
-
- if (typeof sigma === 'undefined')
- throw 'sigma is not declared';
-
- sigma.utils.pkg('sigma.classes');
-
- /**
- * The camera constructor. It just initializes its attributes and methods.
- *
- * @param {string} id The id.
- * @param {sigma.classes.graph} graph The graph.
- * @param {configurable} settings The settings function.
- * @param {?object} options Eventually some overriding options.
- * @return {camera} Returns the fresh new camera instance.
- */
- sigma.classes.camera = function(id, graph, settings, options) {
- sigma.classes.dispatcher.extend(this);
-
- Object.defineProperty(this, 'graph', {
- value: graph
- });
- Object.defineProperty(this, 'id', {
- value: id
- });
- Object.defineProperty(this, 'readPrefix', {
- value: 'read_cam' + id + ':'
- });
- Object.defineProperty(this, 'prefix', {
- value: 'cam' + id + ':'
- });
-
- this.x = 0;
- this.y = 0;
- this.ratio = 1;
- this.angle = 0;
- this.isAnimated = false;
- this.settings = (typeof options === 'object' && options) ?
- settings.embedObject(options) :
- settings;
- };
-
- /**
- * Updates the camera position.
- *
- * @param {object} coordinates The new coordinates object.
- * @return {camera} Returns the camera.
- */
- sigma.classes.camera.prototype.goTo = function(coordinates) {
- if (!this.settings('enableCamera'))
- return this;
-
- var i,
- l,
- c = coordinates || {},
- keys = ['x', 'y', 'ratio', 'angle'];
-
- for (i = 0, l = keys.length; i < l; i++)
- if (c[keys[i]] !== undefined) {
- if (typeof c[keys[i]] === 'number' && !isNaN(c[keys[i]]))
- this[keys[i]] = c[keys[i]];
- else
- throw 'Value for "' + keys[i] + '" is not a number.';
- }
-
- this.dispatchEvent('coordinatesUpdated');
- return this;
- };
-
- /**
- * This method takes a graph and computes for each node and edges its
- * coordinates relatively to the center of the camera. Basically, it will
- * compute the coordinates that will be used by the graphic renderers.
- *
- * Since it should be possible to use different cameras and different
- * renderers, it is possible to specify a prefix to put before the new
- * coordinates (to get something like "node.camera1_x")
- *
- * @param {?string} read The prefix of the coordinates to read.
- * @param {?string} write The prefix of the coordinates to write.
- * @param {?object} options Eventually an object of options. Those can be:
- * - A restricted nodes array.
- * - A restricted edges array.
- * - A width.
- * - A height.
- * @return {camera} Returns the camera.
- */
- sigma.classes.camera.prototype.applyView = function(read, write, options) {
- options = options || {};
- write = write !== undefined ? write : this.prefix;
- read = read !== undefined ? read : this.readPrefix;
-
- var nodes = options.nodes || this.graph.nodes(),
- edges = options.edges || this.graph.edges();
-
- var i,
- l,
- node,
- relCos = Math.cos(this.angle) / this.ratio,
- relSin = Math.sin(this.angle) / this.ratio,
- nodeRatio = Math.pow(this.ratio, this.settings('nodesPowRatio')),
- edgeRatio = Math.pow(this.ratio, this.settings('edgesPowRatio')),
- xOffset = (options.width || 0) / 2 - this.x * relCos - this.y * relSin,
- yOffset = (options.height || 0) / 2 - this.y * relCos + this.x * relSin;
-
- for (i = 0, l = nodes.length; i < l; i++) {
- node = nodes[i];
- node[write + 'x'] =
- (node[read + 'x'] || 0) * relCos +
- (node[read + 'y'] || 0) * relSin +
- xOffset;
- node[write + 'y'] =
- (node[read + 'y'] || 0) * relCos -
- (node[read + 'x'] || 0) * relSin +
- yOffset;
- node[write + 'size'] =
- (node[read + 'size'] || 0) /
- nodeRatio;
- }
-
- for (i = 0, l = edges.length; i < l; i++) {
- edges[i][write + 'size'] =
- (edges[i][read + 'size'] || 0) /
- edgeRatio;
- }
-
- return this;
- };
-
- /**
- * This function converts the coordinates of a point from the frame of the
- * camera to the frame of the graph.
- *
- * @param {number} x The X coordinate of the point in the frame of the
- * camera.
- * @param {number} y The Y coordinate of the point in the frame of the
- * camera.
- * @return {object} The point coordinates in the frame of the graph.
- */
- sigma.classes.camera.prototype.graphPosition = function(x, y, vector) {
- var X = 0,
- Y = 0,
- cos = Math.cos(this.angle),
- sin = Math.sin(this.angle);
-
- // Revert the origin differential vector:
- if (!vector) {
- X = - (this.x * cos + this.y * sin) / this.ratio;
- Y = - (this.y * cos - this.x * sin) / this.ratio;
- }
-
- return {
- x: (x * cos + y * sin) / this.ratio + X,
- y: (y * cos - x * sin) / this.ratio + Y
- };
- };
-
- /**
- * This function converts the coordinates of a point from the frame of the
- * graph to the frame of the camera.
- *
- * @param {number} x The X coordinate of the point in the frame of the
- * graph.
- * @param {number} y The Y coordinate of the point in the frame of the
- * graph.
- * @return {object} The point coordinates in the frame of the camera.
- */
- sigma.classes.camera.prototype.cameraPosition = function(x, y, vector) {
- var X = 0,
- Y = 0,
- cos = Math.cos(this.angle),
- sin = Math.sin(this.angle);
-
- // Revert the origin differential vector:
- if (!vector) {
- X = - (this.x * cos + this.y * sin) / this.ratio;
- Y = - (this.y * cos - this.x * sin) / this.ratio;
- }
-
- return {
- x: ((x - X) * cos - (y - Y) * sin) * this.ratio,
- y: ((y - Y) * cos + (x - X) * sin) * this.ratio
- };
- };
-
- /**
- * This method returns the transformation matrix of the camera. This is
- * especially useful to apply the camera view directly in shaders, in case of
- * WebGL rendering.
- *
- * @return {array} The transformation matrix.
- */
- sigma.classes.camera.prototype.getMatrix = function() {
- var scale = sigma.utils.matrices.scale(1 / this.ratio),
- rotation = sigma.utils.matrices.rotation(this.angle),
- translation = sigma.utils.matrices.translation(-this.x, -this.y),
- matrix = sigma.utils.matrices.multiply(
- translation,
- sigma.utils.matrices.multiply(
- rotation,
- scale
- )
- );
-
- return matrix;
- };
-
- /**
- * Taking a width and a height as parameters, this method returns the
- * coordinates of the rectangle representing the camera on screen, in the
- * graph's referentiel.
- *
- * To keep displaying labels of nodes going out of the screen, the method
- * keeps a margin around the screen in the returned rectangle.
- *
- * @param {number} width The width of the screen.
- * @param {number} height The height of the screen.
- * @return {object} The rectangle as x1, y1, x2 and y2, representing
- * two opposite points.
- */
- sigma.classes.camera.prototype.getRectangle = function(width, height) {
- var widthVect = this.cameraPosition(width, 0, true),
- heightVect = this.cameraPosition(0, height, true),
- centerVect = this.cameraPosition(width / 2, height / 2, true),
- marginX = this.cameraPosition(width / 4, 0, true).x,
- marginY = this.cameraPosition(0, height / 4, true).y;
-
- return {
- x1: this.x - centerVect.x - marginX,
- y1: this.y - centerVect.y - marginY,
- x2: this.x - centerVect.x + marginX + widthVect.x,
- y2: this.y - centerVect.y - marginY + widthVect.y,
- height: Math.sqrt(
- Math.pow(heightVect.x, 2) +
- Math.pow(heightVect.y + 2 * marginY, 2)
- )
- };
- };
- }).call(this);
|