overlapDodgeY.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.OverlapDodgeY = void 0;
  4. const d3_array_1 = require("d3-array");
  5. function isSegmentIntersect([a, b], [c, d]) {
  6. return d > a && b > c;
  7. }
  8. function useMap() {
  9. const map = new Map();
  10. const get = (key) => map.get(key);
  11. const set = (key, value) => map.set(key, value);
  12. return [get, set];
  13. }
  14. function getBoundsWithoutConnector(shape) {
  15. const node = shape.cloneNode(true);
  16. const connectorShape = node.getElementById('connector');
  17. connectorShape && node.removeChild(connectorShape);
  18. const { min, max } = node.getRenderBounds();
  19. node.destroy();
  20. return { min, max };
  21. }
  22. /**
  23. * An iterative dodge method avoids label overlap. (n * log(n))
  24. */
  25. const OverlapDodgeY = (options) => {
  26. const { maxIterations = 10, maxError = 0.1, padding = 1 } = options;
  27. return (labels) => {
  28. const n = labels.length;
  29. if (n <= 1)
  30. return labels;
  31. // Index y, x0, x, height, by label.
  32. const [y0, setY0] = useMap();
  33. const [y, setY] = useMap();
  34. const [h, setH] = useMap();
  35. const [xx, setXX] = useMap();
  36. for (const label of labels) {
  37. const { min, max } = getBoundsWithoutConnector(label);
  38. const [x0, y0] = min;
  39. const [x1, y1] = max;
  40. setY0(label, y0);
  41. setY(label, y0);
  42. setH(label, y1 - y0);
  43. setXX(label, [x0, x1]);
  44. }
  45. // Offsets position Y.
  46. for (let iter = 0; iter < maxIterations; iter++) {
  47. labels.sort((a, b) => (0, d3_array_1.ascending)(y(a), y(b)));
  48. let error = 0;
  49. for (let i = 0; i < n - 1; i++) {
  50. const l0 = labels[i];
  51. let j = i + 1;
  52. let l1;
  53. // Find the next label overlapping with the current label in x direction.
  54. while ((l1 = labels[j]) && !isSegmentIntersect(xx(l0), xx(l1)))
  55. j += 1;
  56. if (l1) {
  57. const y0 = y(l0);
  58. const h0 = h(l0);
  59. const y1 = y(l1);
  60. const delta = y1 - (y0 + h0);
  61. if (delta < padding) {
  62. const newDelta = (padding - delta) / 2;
  63. error = Math.max(error, newDelta);
  64. setY(l0, y0 - newDelta);
  65. setY(l1, y1 + newDelta);
  66. }
  67. }
  68. }
  69. if (error < maxError)
  70. break;
  71. }
  72. for (const label of labels) {
  73. label.style.y += y(label) - y0(label);
  74. }
  75. return labels;
  76. };
  77. };
  78. exports.OverlapDodgeY = OverlapDodgeY;
  79. //# sourceMappingURL=overlapDodgeY.js.map