QuadTree.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * Networkgraph series
  3. *
  4. * (c) 2010-2019 Paweł Fus
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. import H from '../../parts/Globals.js';
  10. var QuadTreeNode = H.QuadTreeNode = function (box) {
  11. this.box = box;
  12. this.nodes = []; // Array of 4 -> quad
  13. this.children = []; // Deferred leafs
  14. this.mass = 1;
  15. this.centerX = 0;
  16. this.centerY = 0;
  17. };
  18. H.extend(
  19. QuadTreeNode.prototype,
  20. {
  21. insert: function (node) {
  22. this.mass++;
  23. if (!this.centerX) {
  24. this.centerX = node.plotX;
  25. this.centerY = node.plotY;
  26. } else {
  27. this.centerX = (this.centerX + node.plotX) / 2;
  28. this.centerY = (this.centerY + node.plotY) / 2;
  29. }
  30. if (this.nodes.length) {
  31. this.nodes[this.getBoxPosition(node)].insert(node);
  32. } else {
  33. if (this.children.length < 3) {
  34. this.children.push(node);
  35. } else {
  36. this.divideBox();
  37. this.children.forEach(function (child) {
  38. this.insert(child);
  39. }, this);
  40. this.insert(node);
  41. }
  42. }
  43. },
  44. divideBox: function () {
  45. var halfWidth = this.box.width / 2,
  46. halfHeight = this.box.height / 2;
  47. this.nodes[0] = new QuadTreeNode({
  48. left: this.box.left,
  49. top: this.box.top,
  50. width: halfWidth,
  51. height: halfHeight
  52. });
  53. this.nodes[1] = new QuadTreeNode({
  54. left: this.box.left + halfWidth,
  55. top: this.box.top,
  56. width: halfWidth,
  57. height: halfHeight
  58. });
  59. this.nodes[2] = new QuadTreeNode({
  60. left: this.box.left + halfWidth,
  61. top: this.box.top + halfHeight,
  62. width: halfWidth,
  63. height: halfHeight
  64. });
  65. this.nodes[3] = new QuadTreeNode({
  66. left: this.box.left,
  67. top: this.box.top + halfHeight,
  68. width: halfWidth,
  69. height: halfHeight
  70. });
  71. },
  72. getBoxPosition: function (node) {
  73. var left = node.plotX < this.box.left + this.box.width / 2,
  74. top = node.plotY < this.box.top + this.box.height / 2,
  75. index;
  76. if (left) {
  77. if (top) {
  78. // Top left
  79. index = 0;
  80. } else {
  81. // Bottom left
  82. index = 3;
  83. }
  84. } else {
  85. if (top) {
  86. // Top right
  87. index = 1;
  88. } else {
  89. // Bottom right
  90. index = 2;
  91. }
  92. }
  93. return index;
  94. }
  95. }
  96. );
  97. var QuadTree = H.QuadTree = function (x, y, width, height) {
  98. // Boundary rectangle:
  99. this.rect = {
  100. left: x,
  101. top: y,
  102. width: width,
  103. height: height
  104. };
  105. this.root = new QuadTreeNode(this.rect);
  106. };
  107. H.extend(
  108. QuadTree.prototype,
  109. {
  110. insertNodes: function (nodes) {
  111. nodes.forEach(function (node) {
  112. this.root.insert(node);
  113. }, this);
  114. },
  115. clear: function (chart) {
  116. this.render(chart, true);
  117. },
  118. visitNodeRecursive: function (node, chart, clear) {
  119. node.nodes.forEach(
  120. function (qtNode) {
  121. if (qtNode.children.length) {
  122. this.renderBox(qtNode, chart, clear);
  123. this.visitNodeRecursive(qtNode, chart, clear);
  124. }
  125. },
  126. this
  127. );
  128. },
  129. render: function (chart, clear) {
  130. this.visitNodeRecursive(this.root, chart, clear);
  131. },
  132. renderBox: function (qtNode, chart, clear) {
  133. if (!qtNode.graphic) {
  134. qtNode.graphic = chart.renderer
  135. .rect(
  136. qtNode.box.left + chart.plotLeft,
  137. qtNode.box.top + chart.plotTop,
  138. qtNode.box.width,
  139. qtNode.box.height
  140. )
  141. .attr({
  142. stroke: 'red',
  143. 'stroke-width': 2
  144. })
  145. .add();
  146. } else if (clear) {
  147. qtNode.graphic = qtNode.graphic.destroy();
  148. }
  149. if (qtNode.graphic) {
  150. qtNode.graphic.animate({
  151. x: qtNode.box.left + chart.plotLeft,
  152. y: qtNode.box.top + chart.plotTop,
  153. width: qtNode.box.width,
  154. height: qtNode.box.height
  155. });
  156. }
  157. }
  158. }
  159. );