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.

832 lines
24 KiB

  1. ;(function(undefined) {
  2. 'use strict';
  3. /**
  4. * Sigma Quadtree Module for edges
  5. * ===============================
  6. *
  7. * Author: Sébastien Heymann,
  8. * from the quad of Guillaume Plique (Yomguithereal)
  9. * Version: 0.2
  10. */
  11. /**
  12. * Quad Geometric Operations
  13. * -------------------------
  14. *
  15. * A useful batch of geometric operations used by the quadtree.
  16. */
  17. var _geom = {
  18. /**
  19. * Transforms a graph node with x, y and size into an
  20. * axis-aligned square.
  21. *
  22. * @param {object} A graph node with at least a point (x, y) and a size.
  23. * @return {object} A square: two points (x1, y1), (x2, y2) and height.
  24. */
  25. pointToSquare: function(n) {
  26. return {
  27. x1: n.x - n.size,
  28. y1: n.y - n.size,
  29. x2: n.x + n.size,
  30. y2: n.y - n.size,
  31. height: n.size * 2
  32. };
  33. },
  34. /**
  35. * Transforms a graph edge with x1, y1, x2, y2 and size into an
  36. * axis-aligned square.
  37. *
  38. * @param {object} A graph edge with at least two points
  39. * (x1, y1), (x2, y2) and a size.
  40. * @return {object} A square: two points (x1, y1), (x2, y2) and height.
  41. */
  42. lineToSquare: function(e) {
  43. if (e.y1 < e.y2) {
  44. // (e.x1, e.y1) on top
  45. if (e.x1 < e.x2) {
  46. // (e.x1, e.y1) on left
  47. return {
  48. x1: e.x1 - e.size,
  49. y1: e.y1 - e.size,
  50. x2: e.x2 + e.size,
  51. y2: e.y1 - e.size,
  52. height: e.y2 - e.y1 + e.size * 2
  53. };
  54. }
  55. // (e.x1, e.y1) on right
  56. return {
  57. x1: e.x2 - e.size,
  58. y1: e.y1 - e.size,
  59. x2: e.x1 + e.size,
  60. y2: e.y1 - e.size,
  61. height: e.y2 - e.y1 + e.size * 2
  62. };
  63. }
  64. // (e.x2, e.y2) on top
  65. if (e.x1 < e.x2) {
  66. // (e.x1, e.y1) on left
  67. return {
  68. x1: e.x1 - e.size,
  69. y1: e.y2 - e.size,
  70. x2: e.x2 + e.size,
  71. y2: e.y2 - e.size,
  72. height: e.y1 - e.y2 + e.size * 2
  73. };
  74. }
  75. // (e.x2, e.y2) on right
  76. return {
  77. x1: e.x2 - e.size,
  78. y1: e.y2 - e.size,
  79. x2: e.x1 + e.size,
  80. y2: e.y2 - e.size,
  81. height: e.y1 - e.y2 + e.size * 2
  82. };
  83. },
  84. /**
  85. * Transforms a graph edge of type 'curve' with x1, y1, x2, y2,
  86. * control point and size into an axis-aligned square.
  87. *
  88. * @param {object} e A graph edge with at least two points
  89. * (x1, y1), (x2, y2) and a size.
  90. * @param {object} cp A control point (x,y).
  91. * @return {object} A square: two points (x1, y1), (x2, y2) and height.
  92. */
  93. quadraticCurveToSquare: function(e, cp) {
  94. var pt = sigma.utils.getPointOnQuadraticCurve(
  95. 0.5,
  96. e.x1,
  97. e.y1,
  98. e.x2,
  99. e.y2,
  100. cp.x,
  101. cp.y
  102. );
  103. // Bounding box of the two points and the point at the middle of the
  104. // curve:
  105. var minX = Math.min(e.x1, e.x2, pt.x),
  106. maxX = Math.max(e.x1, e.x2, pt.x),
  107. minY = Math.min(e.y1, e.y2, pt.y),
  108. maxY = Math.max(e.y1, e.y2, pt.y);
  109. return {
  110. x1: minX - e.size,
  111. y1: minY - e.size,
  112. x2: maxX + e.size,
  113. y2: minY - e.size,
  114. height: maxY - minY + e.size * 2
  115. };
  116. },
  117. /**
  118. * Transforms a graph self loop into an axis-aligned square.
  119. *
  120. * @param {object} n A graph node with a point (x, y) and a size.
  121. * @return {object} A square: two points (x1, y1), (x2, y2) and height.
  122. */
  123. selfLoopToSquare: function(n) {
  124. // Fitting to the curve is too costly, we compute a larger bounding box
  125. // using the control points:
  126. var cp = sigma.utils.getSelfLoopControlPoints(n.x, n.y, n.size);
  127. // Bounding box of the point and the two control points:
  128. var minX = Math.min(n.x, cp.x1, cp.x2),
  129. maxX = Math.max(n.x, cp.x1, cp.x2),
  130. minY = Math.min(n.y, cp.y1, cp.y2),
  131. maxY = Math.max(n.y, cp.y1, cp.y2);
  132. return {
  133. x1: minX - n.size,
  134. y1: minY - n.size,
  135. x2: maxX + n.size,
  136. y2: minY - n.size,
  137. height: maxY - minY + n.size * 2
  138. };
  139. },
  140. /**
  141. * Checks whether a rectangle is axis-aligned.
  142. *
  143. * @param {object} A rectangle defined by two points
  144. * (x1, y1) and (x2, y2).
  145. * @return {boolean} True if the rectangle is axis-aligned.
  146. */
  147. isAxisAligned: function(r) {
  148. return r.x1 === r.x2 || r.y1 === r.y2;
  149. },
  150. /**
  151. * Compute top points of an axis-aligned rectangle. This is useful in
  152. * cases when the rectangle has been rotated (left, right or bottom up) and
  153. * later operations need to know the top points.
  154. *
  155. * @param {object} An axis-aligned rectangle defined by two points
  156. * (x1, y1), (x2, y2) and height.
  157. * @return {object} A rectangle: two points (x1, y1), (x2, y2) and height.
  158. */
  159. axisAlignedTopPoints: function(r) {
  160. // Basic
  161. if (r.y1 === r.y2 && r.x1 < r.x2)
  162. return r;
  163. // Rotated to right
  164. if (r.x1 === r.x2 && r.y2 > r.y1)
  165. return {
  166. x1: r.x1 - r.height, y1: r.y1,
  167. x2: r.x1, y2: r.y1,
  168. height: r.height
  169. };
  170. // Rotated to left
  171. if (r.x1 === r.x2 && r.y2 < r.y1)
  172. return {
  173. x1: r.x1, y1: r.y2,
  174. x2: r.x2 + r.height, y2: r.y2,
  175. height: r.height
  176. };
  177. // Bottom's up
  178. return {
  179. x1: r.x2, y1: r.y1 - r.height,
  180. x2: r.x1, y2: r.y1 - r.height,
  181. height: r.height
  182. };
  183. },
  184. /**
  185. * Get coordinates of a rectangle's lower left corner from its top points.
  186. *
  187. * @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
  188. * @return {object} Coordinates of the corner (x, y).
  189. */
  190. lowerLeftCoor: function(r) {
  191. var width = (
  192. Math.sqrt(
  193. Math.pow(r.x2 - r.x1, 2) +
  194. Math.pow(r.y2 - r.y1, 2)
  195. )
  196. );
  197. return {
  198. x: r.x1 - (r.y2 - r.y1) * r.height / width,
  199. y: r.y1 + (r.x2 - r.x1) * r.height / width
  200. };
  201. },
  202. /**
  203. * Get coordinates of a rectangle's lower right corner from its top points
  204. * and its lower left corner.
  205. *
  206. * @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
  207. * @param {object} A corner's coordinates (x, y).
  208. * @return {object} Coordinates of the corner (x, y).
  209. */
  210. lowerRightCoor: function(r, llc) {
  211. return {
  212. x: llc.x - r.x1 + r.x2,
  213. y: llc.y - r.y1 + r.y2
  214. };
  215. },
  216. /**
  217. * Get the coordinates of all the corners of a rectangle from its top point.
  218. *
  219. * @param {object} A rectangle defined by two points (x1, y1) and (x2, y2).
  220. * @return {array} An array of the four corners' coordinates (x, y).
  221. */
  222. rectangleCorners: function(r) {
  223. var llc = this.lowerLeftCoor(r),
  224. lrc = this.lowerRightCoor(r, llc);
  225. return [
  226. {x: r.x1, y: r.y1},
  227. {x: r.x2, y: r.y2},
  228. {x: llc.x, y: llc.y},
  229. {x: lrc.x, y: lrc.y}
  230. ];
  231. },
  232. /**
  233. * Split a square defined by its boundaries into four.
  234. *
  235. * @param {object} Boundaries of the square (x, y, width, height).
  236. * @return {array} An array containing the four new squares, themselves
  237. * defined by an array of their four corners (x, y).
  238. */
  239. splitSquare: function(b) {
  240. return [
  241. [
  242. {x: b.x, y: b.y},
  243. {x: b.x + b.width / 2, y: b.y},
  244. {x: b.x, y: b.y + b.height / 2},
  245. {x: b.x + b.width / 2, y: b.y + b.height / 2}
  246. ],
  247. [
  248. {x: b.x + b.width / 2, y: b.y},
  249. {x: b.x + b.width, y: b.y},
  250. {x: b.x + b.width / 2, y: b.y + b.height / 2},
  251. {x: b.x + b.width, y: b.y + b.height / 2}
  252. ],
  253. [
  254. {x: b.x, y: b.y + b.height / 2},
  255. {x: b.x + b.width / 2, y: b.y + b.height / 2},
  256. {x: b.x, y: b.y + b.height},
  257. {x: b.x + b.width / 2, y: b.y + b.height}
  258. ],
  259. [
  260. {x: b.x + b.width / 2, y: b.y + b.height / 2},
  261. {x: b.x + b.width, y: b.y + b.height / 2},
  262. {x: b.x + b.width / 2, y: b.y + b.height},
  263. {x: b.x + b.width, y: b.y + b.height}
  264. ]
  265. ];
  266. },
  267. /**
  268. * Compute the four axis between corners of rectangle A and corners of
  269. * rectangle B. This is needed later to check an eventual collision.
  270. *
  271. * @param {array} An array of rectangle A's four corners (x, y).
  272. * @param {array} An array of rectangle B's four corners (x, y).
  273. * @return {array} An array of four axis defined by their coordinates (x,y).
  274. */
  275. axis: function(c1, c2) {
  276. return [
  277. {x: c1[1].x - c1[0].x, y: c1[1].y - c1[0].y},
  278. {x: c1[1].x - c1[3].x, y: c1[1].y - c1[3].y},
  279. {x: c2[0].x - c2[2].x, y: c2[0].y - c2[2].y},
  280. {x: c2[0].x - c2[1].x, y: c2[0].y - c2[1].y}
  281. ];
  282. },
  283. /**
  284. * Project a rectangle's corner on an axis.
  285. *
  286. * @param {object} Coordinates of a corner (x, y).
  287. * @param {object} Coordinates of an axis (x, y).
  288. * @return {object} The projection defined by coordinates (x, y).
  289. */
  290. projection: function(c, a) {
  291. var l = (
  292. (c.x * a.x + c.y * a.y) /
  293. (Math.pow(a.x, 2) + Math.pow(a.y, 2))
  294. );
  295. return {
  296. x: l * a.x,
  297. y: l * a.y
  298. };
  299. },
  300. /**
  301. * Check whether two rectangles collide on one particular axis.
  302. *
  303. * @param {object} An axis' coordinates (x, y).
  304. * @param {array} Rectangle A's corners.
  305. * @param {array} Rectangle B's corners.
  306. * @return {boolean} True if the rectangles collide on the axis.
  307. */
  308. axisCollision: function(a, c1, c2) {
  309. var sc1 = [],
  310. sc2 = [];
  311. for (var ci = 0; ci < 4; ci++) {
  312. var p1 = this.projection(c1[ci], a),
  313. p2 = this.projection(c2[ci], a);
  314. sc1.push(p1.x * a.x + p1.y * a.y);
  315. sc2.push(p2.x * a.x + p2.y * a.y);
  316. }
  317. var maxc1 = Math.max.apply(Math, sc1),
  318. maxc2 = Math.max.apply(Math, sc2),
  319. minc1 = Math.min.apply(Math, sc1),
  320. minc2 = Math.min.apply(Math, sc2);
  321. return (minc2 <= maxc1 && maxc2 >= minc1);
  322. },
  323. /**
  324. * Check whether two rectangles collide on each one of their four axis. If
  325. * all axis collide, then the two rectangles do collide on the plane.
  326. *
  327. * @param {array} Rectangle A's corners.
  328. * @param {array} Rectangle B's corners.
  329. * @return {boolean} True if the rectangles collide.
  330. */
  331. collision: function(c1, c2) {
  332. var axis = this.axis(c1, c2),
  333. col = true;
  334. for (var i = 0; i < 4; i++)
  335. col = col && this.axisCollision(axis[i], c1, c2);
  336. return col;
  337. }
  338. };
  339. /**
  340. * Quad Functions
  341. * ------------
  342. *
  343. * The Quadtree functions themselves.
  344. * For each of those functions, we consider that in a splitted quad, the
  345. * index of each node is the following:
  346. * 0: top left
  347. * 1: top right
  348. * 2: bottom left
  349. * 3: bottom right
  350. *
  351. * Moreover, the hereafter quad's philosophy is to consider that if an element
  352. * collides with more than one nodes, this element belongs to each of the
  353. * nodes it collides with where other would let it lie on a higher node.
  354. */
  355. /**
  356. * Get the index of the node containing the point in the quad
  357. *
  358. * @param {object} point A point defined by coordinates (x, y).
  359. * @param {object} quadBounds Boundaries of the quad (x, y, width, heigth).
  360. * @return {integer} The index of the node containing the point.
  361. */
  362. function _quadIndex(point, quadBounds) {
  363. var xmp = quadBounds.x + quadBounds.width / 2,
  364. ymp = quadBounds.y + quadBounds.height / 2,
  365. top = (point.y < ymp),
  366. left = (point.x < xmp);
  367. if (top) {
  368. if (left)
  369. return 0;
  370. else
  371. return 1;
  372. }
  373. else {
  374. if (left)
  375. return 2;
  376. else
  377. return 3;
  378. }
  379. }
  380. /**
  381. * Get a list of indexes of nodes containing an axis-aligned rectangle
  382. *
  383. * @param {object} rectangle A rectangle defined by two points (x1, y1),
  384. * (x2, y2) and height.
  385. * @param {array} quadCorners An array of the quad nodes' corners.
  386. * @return {array} An array of indexes containing one to
  387. * four integers.
  388. */
  389. function _quadIndexes(rectangle, quadCorners) {
  390. var indexes = [];
  391. // Iterating through quads
  392. for (var i = 0; i < 4; i++)
  393. if ((rectangle.x2 >= quadCorners[i][0].x) &&
  394. (rectangle.x1 <= quadCorners[i][1].x) &&
  395. (rectangle.y1 + rectangle.height >= quadCorners[i][0].y) &&
  396. (rectangle.y1 <= quadCorners[i][2].y))
  397. indexes.push(i);
  398. return indexes;
  399. }
  400. /**
  401. * Get a list of indexes of nodes containing a non-axis-aligned rectangle
  402. *
  403. * @param {array} corners An array containing each corner of the
  404. * rectangle defined by its coordinates (x, y).
  405. * @param {array} quadCorners An array of the quad nodes' corners.
  406. * @return {array} An array of indexes containing one to
  407. * four integers.
  408. */
  409. function _quadCollision(corners, quadCorners) {
  410. var indexes = [];
  411. // Iterating through quads
  412. for (var i = 0; i < 4; i++)
  413. if (_geom.collision(corners, quadCorners[i]))
  414. indexes.push(i);
  415. return indexes;
  416. }
  417. /**
  418. * Subdivide a quad by creating a node at a precise index. The function does
  419. * not generate all four nodes not to potentially create unused nodes.
  420. *
  421. * @param {integer} index The index of the node to create.
  422. * @param {object} quad The quad object to subdivide.
  423. * @return {object} A new quad representing the node created.
  424. */
  425. function _quadSubdivide(index, quad) {
  426. var next = quad.level + 1,
  427. subw = Math.round(quad.bounds.width / 2),
  428. subh = Math.round(quad.bounds.height / 2),
  429. qx = Math.round(quad.bounds.x),
  430. qy = Math.round(quad.bounds.y),
  431. x,
  432. y;
  433. switch (index) {
  434. case 0:
  435. x = qx;
  436. y = qy;
  437. break;
  438. case 1:
  439. x = qx + subw;
  440. y = qy;
  441. break;
  442. case 2:
  443. x = qx;
  444. y = qy + subh;
  445. break;
  446. case 3:
  447. x = qx + subw;
  448. y = qy + subh;
  449. break;
  450. }
  451. return _quadTree(
  452. {x: x, y: y, width: subw, height: subh},
  453. next,
  454. quad.maxElements,
  455. quad.maxLevel
  456. );
  457. }
  458. /**
  459. * Recursively insert an element into the quadtree. Only points
  460. * with size, i.e. axis-aligned squares, may be inserted with this
  461. * method.
  462. *
  463. * @param {object} el The element to insert in the quadtree.
  464. * @param {object} sizedPoint A sized point defined by two top points
  465. * (x1, y1), (x2, y2) and height.
  466. * @param {object} quad The quad in which to insert the element.
  467. * @return {undefined} The function does not return anything.
  468. */
  469. function _quadInsert(el, sizedPoint, quad) {
  470. if (quad.level < quad.maxLevel) {
  471. // Searching appropriate quads
  472. var indexes = _quadIndexes(sizedPoint, quad.corners);
  473. // Iterating
  474. for (var i = 0, l = indexes.length; i < l; i++) {
  475. // Subdividing if necessary
  476. if (quad.nodes[indexes[i]] === undefined)
  477. quad.nodes[indexes[i]] = _quadSubdivide(indexes[i], quad);
  478. // Recursion
  479. _quadInsert(el, sizedPoint, quad.nodes[indexes[i]]);
  480. }
  481. }
  482. else {
  483. // Pushing the element in a leaf node
  484. quad.elements.push(el);
  485. }
  486. }
  487. /**
  488. * Recursively retrieve every elements held by the node containing the
  489. * searched point.
  490. *
  491. * @param {object} point The searched point (x, y).
  492. * @param {object} quad The searched quad.
  493. * @return {array} An array of elements contained in the relevant
  494. * node.
  495. */
  496. function _quadRetrievePoint(point, quad) {
  497. if (quad.level < quad.maxLevel) {
  498. var index = _quadIndex(point, quad.bounds);
  499. // If node does not exist we return an empty list
  500. if (quad.nodes[index] !== undefined) {
  501. return _quadRetrievePoint(point, quad.nodes[index]);
  502. }
  503. else {
  504. return [];
  505. }
  506. }
  507. else {
  508. return quad.elements;
  509. }
  510. }
  511. /**
  512. * Recursively retrieve every elements contained within an rectangular area
  513. * that may or may not be axis-aligned.
  514. *
  515. * @param {object|array} rectData The searched area defined either by
  516. * an array of four corners (x, y) in
  517. * the case of a non-axis-aligned
  518. * rectangle or an object with two top
  519. * points (x1, y1), (x2, y2) and height.
  520. * @param {object} quad The searched quad.
  521. * @param {function} collisionFunc The collision function used to search
  522. * for node indexes.
  523. * @param {array?} els The retrieved elements.
  524. * @return {array} An array of elements contained in the
  525. * area.
  526. */
  527. function _quadRetrieveArea(rectData, quad, collisionFunc, els) {
  528. els = els || {};
  529. if (quad.level < quad.maxLevel) {
  530. var indexes = collisionFunc(rectData, quad.corners);
  531. for (var i = 0, l = indexes.length; i < l; i++)
  532. if (quad.nodes[indexes[i]] !== undefined)
  533. _quadRetrieveArea(
  534. rectData,
  535. quad.nodes[indexes[i]],
  536. collisionFunc,
  537. els
  538. );
  539. } else
  540. for (var j = 0, m = quad.elements.length; j < m; j++)
  541. if (els[quad.elements[j].id] === undefined)
  542. els[quad.elements[j].id] = quad.elements[j];
  543. return els;
  544. }
  545. /**
  546. * Creates the quadtree object itself.
  547. *
  548. * @param {object} bounds The boundaries of the quad defined by an
  549. * origin (x, y), width and heigth.
  550. * @param {integer} level The level of the quad in the tree.
  551. * @param {integer} maxElements The max number of element in a leaf node.
  552. * @param {integer} maxLevel The max recursion level of the tree.
  553. * @return {object} The quadtree object.
  554. */
  555. function _quadTree(bounds, level, maxElements, maxLevel) {
  556. return {
  557. level: level || 0,
  558. bounds: bounds,
  559. corners: _geom.splitSquare(bounds),
  560. maxElements: maxElements || 40,
  561. maxLevel: maxLevel || 8,
  562. elements: [],
  563. nodes: []
  564. };
  565. }
  566. /**
  567. * Sigma Quad Constructor
  568. * ----------------------
  569. *
  570. * The edgequad API as exposed to sigma.
  571. */
  572. /**
  573. * The edgequad core that will become the sigma interface with the quadtree.
  574. *
  575. * property {object} _tree Property holding the quadtree object.
  576. * property {object} _geom Exposition of the _geom namespace for testing.
  577. * property {object} _cache Cache for the area method.
  578. * property {boolean} _enabled Can index and retreive elements.
  579. */
  580. var edgequad = function() {
  581. this._geom = _geom;
  582. this._tree = null;
  583. this._cache = {
  584. query: false,
  585. result: false
  586. };
  587. this._enabled = true;
  588. };
  589. /**
  590. * Index a graph by inserting its edges into the quadtree.
  591. *
  592. * @param {object} graph A graph instance.
  593. * @param {object} params An object of parameters with at least the quad
  594. * bounds.
  595. * @return {object} The quadtree object.
  596. *
  597. * Parameters:
  598. * ----------
  599. * bounds: {object} boundaries of the quad defined by its origin (x, y)
  600. * width and heigth.
  601. * prefix: {string?} a prefix for edge geometric attributes.
  602. * maxElements: {integer?} the max number of elements in a leaf node.
  603. * maxLevel: {integer?} the max recursion level of the tree.
  604. */
  605. edgequad.prototype.index = function(graph, params) {
  606. if (!this._enabled)
  607. return this._tree;
  608. // Enforcing presence of boundaries
  609. if (!params.bounds)
  610. throw 'sigma.classes.edgequad.index: bounds information not given.';
  611. // Prefix
  612. var prefix = params.prefix || '',
  613. cp,
  614. source,
  615. target,
  616. n,
  617. e;
  618. // Building the tree
  619. this._tree = _quadTree(
  620. params.bounds,
  621. 0,
  622. params.maxElements,
  623. params.maxLevel
  624. );
  625. var edges = graph.edges();
  626. // Inserting graph edges into the tree
  627. for (var i = 0, l = edges.length; i < l; i++) {
  628. source = graph.nodes(edges[i].source);
  629. target = graph.nodes(edges[i].target);
  630. e = {
  631. x1: source[prefix + 'x'],
  632. y1: source[prefix + 'y'],
  633. x2: target[prefix + 'x'],
  634. y2: target[prefix + 'y'],
  635. size: edges[i][prefix + 'size'] || 0
  636. };
  637. // Inserting edge
  638. if (edges[i].type === 'curve' || edges[i].type === 'curvedArrow') {
  639. if (source.id === target.id) {
  640. n = {
  641. x: source[prefix + 'x'],
  642. y: source[prefix + 'y'],
  643. size: source[prefix + 'size'] || 0
  644. };
  645. _quadInsert(
  646. edges[i],
  647. _geom.selfLoopToSquare(n),
  648. this._tree);
  649. }
  650. else {
  651. cp = sigma.utils.getQuadraticControlPoint(e.x1, e.y1, e.x2, e.y2);
  652. _quadInsert(
  653. edges[i],
  654. _geom.quadraticCurveToSquare(e, cp),
  655. this._tree);
  656. }
  657. }
  658. else {
  659. _quadInsert(
  660. edges[i],
  661. _geom.lineToSquare(e),
  662. this._tree);
  663. }
  664. }
  665. // Reset cache:
  666. this._cache = {
  667. query: false,
  668. result: false
  669. };
  670. // remove?
  671. return this._tree;
  672. };
  673. /**
  674. * Retrieve every graph edges held by the quadtree node containing the
  675. * searched point.
  676. *
  677. * @param {number} x of the point.
  678. * @param {number} y of the point.
  679. * @return {array} An array of edges retrieved.
  680. */
  681. edgequad.prototype.point = function(x, y) {
  682. if (!this._enabled)
  683. return [];
  684. return this._tree ?
  685. _quadRetrievePoint({x: x, y: y}, this._tree) || [] :
  686. [];
  687. };
  688. /**
  689. * Retrieve every graph edges within a rectangular area. The methods keep the
  690. * last area queried in cache for optimization reason and will act differently
  691. * for the same reason if the area is axis-aligned or not.
  692. *
  693. * @param {object} A rectangle defined by two top points (x1, y1), (x2, y2)
  694. * and height.
  695. * @return {array} An array of edges retrieved.
  696. */
  697. edgequad.prototype.area = function(rect) {
  698. if (!this._enabled)
  699. return [];
  700. var serialized = JSON.stringify(rect),
  701. collisionFunc,
  702. rectData;
  703. // Returning cache?
  704. if (this._cache.query === serialized)
  705. return this._cache.result;
  706. // Axis aligned ?
  707. if (_geom.isAxisAligned(rect)) {
  708. collisionFunc = _quadIndexes;
  709. rectData = _geom.axisAlignedTopPoints(rect);
  710. }
  711. else {
  712. collisionFunc = _quadCollision;
  713. rectData = _geom.rectangleCorners(rect);
  714. }
  715. // Retrieving edges
  716. var edges = this._tree ?
  717. _quadRetrieveArea(
  718. rectData,
  719. this._tree,
  720. collisionFunc
  721. ) :
  722. [];
  723. // Object to array
  724. var edgesArray = [];
  725. for (var i in edges)
  726. edgesArray.push(edges[i]);
  727. // Caching
  728. this._cache.query = serialized;
  729. this._cache.result = edgesArray;
  730. return edgesArray;
  731. };
  732. /**
  733. * EXPORT:
  734. * *******
  735. */
  736. if (typeof this.sigma !== 'undefined') {
  737. this.sigma.classes = this.sigma.classes || {};
  738. this.sigma.classes.edgequad = edgequad;
  739. } else if (typeof exports !== 'undefined') {
  740. if (typeof module !== 'undefined' && module.exports)
  741. exports = module.exports = edgequad;
  742. exports.edgequad = edgequad;
  743. } else
  744. this.edgequad = edgequad;
  745. }).call(this);