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

  1. ;(function(undefined) {
  2. 'use strict';
  3. if (typeof sigma === 'undefined')
  4. throw 'sigma is not declared';
  5. sigma.utils.pkg('sigma.classes');
  6. /**
  7. * The camera constructor. It just initializes its attributes and methods.
  8. *
  9. * @param {string} id The id.
  10. * @param {sigma.classes.graph} graph The graph.
  11. * @param {configurable} settings The settings function.
  12. * @param {?object} options Eventually some overriding options.
  13. * @return {camera} Returns the fresh new camera instance.
  14. */
  15. sigma.classes.camera = function(id, graph, settings, options) {
  16. sigma.classes.dispatcher.extend(this);
  17. Object.defineProperty(this, 'graph', {
  18. value: graph
  19. });
  20. Object.defineProperty(this, 'id', {
  21. value: id
  22. });
  23. Object.defineProperty(this, 'readPrefix', {
  24. value: 'read_cam' + id + ':'
  25. });
  26. Object.defineProperty(this, 'prefix', {
  27. value: 'cam' + id + ':'
  28. });
  29. this.x = 0;
  30. this.y = 0;
  31. this.ratio = 1;
  32. this.angle = 0;
  33. this.isAnimated = false;
  34. this.settings = (typeof options === 'object' && options) ?
  35. settings.embedObject(options) :
  36. settings;
  37. };
  38. /**
  39. * Updates the camera position.
  40. *
  41. * @param {object} coordinates The new coordinates object.
  42. * @return {camera} Returns the camera.
  43. */
  44. sigma.classes.camera.prototype.goTo = function(coordinates) {
  45. if (!this.settings('enableCamera'))
  46. return this;
  47. var i,
  48. l,
  49. c = coordinates || {},
  50. keys = ['x', 'y', 'ratio', 'angle'];
  51. for (i = 0, l = keys.length; i < l; i++)
  52. if (c[keys[i]] !== undefined) {
  53. if (typeof c[keys[i]] === 'number' && !isNaN(c[keys[i]]))
  54. this[keys[i]] = c[keys[i]];
  55. else
  56. throw 'Value for "' + keys[i] + '" is not a number.';
  57. }
  58. this.dispatchEvent('coordinatesUpdated');
  59. return this;
  60. };
  61. /**
  62. * This method takes a graph and computes for each node and edges its
  63. * coordinates relatively to the center of the camera. Basically, it will
  64. * compute the coordinates that will be used by the graphic renderers.
  65. *
  66. * Since it should be possible to use different cameras and different
  67. * renderers, it is possible to specify a prefix to put before the new
  68. * coordinates (to get something like "node.camera1_x")
  69. *
  70. * @param {?string} read The prefix of the coordinates to read.
  71. * @param {?string} write The prefix of the coordinates to write.
  72. * @param {?object} options Eventually an object of options. Those can be:
  73. * - A restricted nodes array.
  74. * - A restricted edges array.
  75. * - A width.
  76. * - A height.
  77. * @return {camera} Returns the camera.
  78. */
  79. sigma.classes.camera.prototype.applyView = function(read, write, options) {
  80. options = options || {};
  81. write = write !== undefined ? write : this.prefix;
  82. read = read !== undefined ? read : this.readPrefix;
  83. var nodes = options.nodes || this.graph.nodes(),
  84. edges = options.edges || this.graph.edges();
  85. var i,
  86. l,
  87. node,
  88. relCos = Math.cos(this.angle) / this.ratio,
  89. relSin = Math.sin(this.angle) / this.ratio,
  90. nodeRatio = Math.pow(this.ratio, this.settings('nodesPowRatio')),
  91. edgeRatio = Math.pow(this.ratio, this.settings('edgesPowRatio')),
  92. xOffset = (options.width || 0) / 2 - this.x * relCos - this.y * relSin,
  93. yOffset = (options.height || 0) / 2 - this.y * relCos + this.x * relSin;
  94. for (i = 0, l = nodes.length; i < l; i++) {
  95. node = nodes[i];
  96. node[write + 'x'] =
  97. (node[read + 'x'] || 0) * relCos +
  98. (node[read + 'y'] || 0) * relSin +
  99. xOffset;
  100. node[write + 'y'] =
  101. (node[read + 'y'] || 0) * relCos -
  102. (node[read + 'x'] || 0) * relSin +
  103. yOffset;
  104. node[write + 'size'] =
  105. (node[read + 'size'] || 0) /
  106. nodeRatio;
  107. }
  108. for (i = 0, l = edges.length; i < l; i++) {
  109. edges[i][write + 'size'] =
  110. (edges[i][read + 'size'] || 0) /
  111. edgeRatio;
  112. }
  113. return this;
  114. };
  115. /**
  116. * This function converts the coordinates of a point from the frame of the
  117. * camera to the frame of the graph.
  118. *
  119. * @param {number} x The X coordinate of the point in the frame of the
  120. * camera.
  121. * @param {number} y The Y coordinate of the point in the frame of the
  122. * camera.
  123. * @return {object} The point coordinates in the frame of the graph.
  124. */
  125. sigma.classes.camera.prototype.graphPosition = function(x, y, vector) {
  126. var X = 0,
  127. Y = 0,
  128. cos = Math.cos(this.angle),
  129. sin = Math.sin(this.angle);
  130. // Revert the origin differential vector:
  131. if (!vector) {
  132. X = - (this.x * cos + this.y * sin) / this.ratio;
  133. Y = - (this.y * cos - this.x * sin) / this.ratio;
  134. }
  135. return {
  136. x: (x * cos + y * sin) / this.ratio + X,
  137. y: (y * cos - x * sin) / this.ratio + Y
  138. };
  139. };
  140. /**
  141. * This function converts the coordinates of a point from the frame of the
  142. * graph to the frame of the camera.
  143. *
  144. * @param {number} x The X coordinate of the point in the frame of the
  145. * graph.
  146. * @param {number} y The Y coordinate of the point in the frame of the
  147. * graph.
  148. * @return {object} The point coordinates in the frame of the camera.
  149. */
  150. sigma.classes.camera.prototype.cameraPosition = function(x, y, vector) {
  151. var X = 0,
  152. Y = 0,
  153. cos = Math.cos(this.angle),
  154. sin = Math.sin(this.angle);
  155. // Revert the origin differential vector:
  156. if (!vector) {
  157. X = - (this.x * cos + this.y * sin) / this.ratio;
  158. Y = - (this.y * cos - this.x * sin) / this.ratio;
  159. }
  160. return {
  161. x: ((x - X) * cos - (y - Y) * sin) * this.ratio,
  162. y: ((y - Y) * cos + (x - X) * sin) * this.ratio
  163. };
  164. };
  165. /**
  166. * This method returns the transformation matrix of the camera. This is
  167. * especially useful to apply the camera view directly in shaders, in case of
  168. * WebGL rendering.
  169. *
  170. * @return {array} The transformation matrix.
  171. */
  172. sigma.classes.camera.prototype.getMatrix = function() {
  173. var scale = sigma.utils.matrices.scale(1 / this.ratio),
  174. rotation = sigma.utils.matrices.rotation(this.angle),
  175. translation = sigma.utils.matrices.translation(-this.x, -this.y),
  176. matrix = sigma.utils.matrices.multiply(
  177. translation,
  178. sigma.utils.matrices.multiply(
  179. rotation,
  180. scale
  181. )
  182. );
  183. return matrix;
  184. };
  185. /**
  186. * Taking a width and a height as parameters, this method returns the
  187. * coordinates of the rectangle representing the camera on screen, in the
  188. * graph's referentiel.
  189. *
  190. * To keep displaying labels of nodes going out of the screen, the method
  191. * keeps a margin around the screen in the returned rectangle.
  192. *
  193. * @param {number} width The width of the screen.
  194. * @param {number} height The height of the screen.
  195. * @return {object} The rectangle as x1, y1, x2 and y2, representing
  196. * two opposite points.
  197. */
  198. sigma.classes.camera.prototype.getRectangle = function(width, height) {
  199. var widthVect = this.cameraPosition(width, 0, true),
  200. heightVect = this.cameraPosition(0, height, true),
  201. centerVect = this.cameraPosition(width / 2, height / 2, true),
  202. marginX = this.cameraPosition(width / 4, 0, true).x,
  203. marginY = this.cameraPosition(0, height / 4, true).y;
  204. return {
  205. x1: this.x - centerVect.x - marginX,
  206. y1: this.y - centerVect.y - marginY,
  207. x2: this.x - centerVect.x + marginX + widthVect.x,
  208. y2: this.y - centerVect.y - marginY + widthVect.y,
  209. height: Math.sqrt(
  210. Math.pow(heightVect.x, 2) +
  211. Math.pow(heightVect.y + 2 * marginY, 2)
  212. )
  213. };
  214. };
  215. }).call(this);