Graph database Analysis of the Steam Network
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

240 lines
7.8 KiB

;(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);