@krainovsd/graph 0.13.0-beta1 → 0.14.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/lib/cjs/index.cjs +984 -497
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/esm/constants/force-controls.js +4 -2
  4. package/lib/esm/constants/force-controls.js.map +1 -1
  5. package/lib/esm/constants/highlight-controls.js +9 -16
  6. package/lib/esm/constants/highlight-controls.js.map +1 -1
  7. package/lib/esm/constants/link-controls.js +17 -30
  8. package/lib/esm/constants/link-controls.js.map +1 -1
  9. package/lib/esm/constants/node-controls.js +4 -2
  10. package/lib/esm/constants/node-controls.js.map +1 -1
  11. package/lib/esm/index.js +8 -7
  12. package/lib/esm/index.js.map +1 -1
  13. package/lib/esm/module/GraphCanvas/GraphCanvas.js +222 -34
  14. package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -1
  15. package/lib/esm/module/GraphCanvas/constants/force-settings.js +11 -1
  16. package/lib/esm/module/GraphCanvas/constants/force-settings.js.map +1 -1
  17. package/lib/esm/module/GraphCanvas/constants/graph-settings.js +17 -5
  18. package/lib/esm/module/GraphCanvas/constants/graph-settings.js.map +1 -1
  19. package/lib/esm/module/GraphCanvas/constants/highlight-settings.js +5 -3
  20. package/lib/esm/module/GraphCanvas/constants/highlight-settings.js.map +1 -1
  21. package/lib/esm/module/GraphCanvas/constants/index.js +5 -2
  22. package/lib/esm/module/GraphCanvas/constants/index.js.map +1 -1
  23. package/lib/esm/module/GraphCanvas/constants/link-settings.js +19 -9
  24. package/lib/esm/module/GraphCanvas/constants/link-settings.js.map +1 -1
  25. package/lib/esm/module/GraphCanvas/constants/node-settings.js +13 -3
  26. package/lib/esm/module/GraphCanvas/constants/node-settings.js.map +1 -1
  27. package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js +4 -2
  28. package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js.map +1 -1
  29. package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js +4 -0
  30. package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js.map +1 -1
  31. package/lib/esm/module/GraphCanvas/lib/settings/highlight-settings-getter.js +4 -2
  32. package/lib/esm/module/GraphCanvas/lib/settings/highlight-settings-getter.js.map +1 -1
  33. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js +8 -5
  34. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js.map +1 -1
  35. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js +14 -2
  36. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js.map +1 -1
  37. package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js +45 -0
  38. package/lib/esm/module/GraphCanvas/lib/utils/calculate-curve-link-position-by-node.js.map +1 -0
  39. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js +42 -37
  40. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-node.js.map +1 -1
  41. package/lib/esm/module/GraphCanvas/lib/utils/compute-graph-bounds.js +26 -0
  42. package/lib/esm/module/GraphCanvas/lib/utils/compute-graph-bounds.js.map +1 -0
  43. package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js +40 -30
  44. package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js.map +1 -1
  45. package/lib/esm/module/GraphCanvas/lib/utils/is-empty-object.js +11 -0
  46. package/lib/esm/module/GraphCanvas/lib/utils/is-empty-object.js.map +1 -0
  47. package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js +5 -2
  48. package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js.map +1 -1
  49. package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js +72 -22
  50. package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js.map +1 -1
  51. package/lib/esm/module/GraphCanvas/slices/draw-links.js +115 -77
  52. package/lib/esm/module/GraphCanvas/slices/draw-links.js.map +1 -1
  53. package/lib/esm/module/GraphCanvas/slices/draw-nodes.js +9 -4
  54. package/lib/esm/module/GraphCanvas/slices/draw-nodes.js.map +1 -1
  55. package/lib/esm/module/GraphCanvas/slices/init-dnd.js +21 -4
  56. package/lib/esm/module/GraphCanvas/slices/init-dnd.js.map +1 -1
  57. package/lib/esm/module/GraphCanvas/slices/init-draw.js +1 -52
  58. package/lib/esm/module/GraphCanvas/slices/init-draw.js.map +1 -1
  59. package/lib/esm/module/GraphCanvas/slices/init-pointer.js +12 -34
  60. package/lib/esm/module/GraphCanvas/slices/init-pointer.js.map +1 -1
  61. package/lib/esm/module/GraphCanvas/slices/init-resize.js +7 -0
  62. package/lib/esm/module/GraphCanvas/slices/init-resize.js.map +1 -1
  63. package/lib/esm/module/GraphCanvas/slices/init-simulation.js +12 -2
  64. package/lib/esm/module/GraphCanvas/slices/init-simulation.js.map +1 -1
  65. package/lib/esm/module/GraphCanvas/slices/init-zoom.js +85 -16
  66. package/lib/esm/module/GraphCanvas/slices/init-zoom.js.map +1 -1
  67. package/lib/esm/module/GraphCanvas/slices/update-link-cache.js +63 -3
  68. package/lib/esm/module/GraphCanvas/slices/update-link-cache.js.map +1 -1
  69. package/lib/esm/module/GraphCanvas/slices/update-node-cache.js +61 -16
  70. package/lib/esm/module/GraphCanvas/slices/update-node-cache.js.map +1 -1
  71. package/lib/index.d.ts +90 -36
  72. package/package.json +3 -2
@@ -1,39 +1,49 @@
1
+ const FRAME_INTERVAL = 1000 / 60;
1
2
  function getParticlePosition(opts) {
2
- const prevStepDifference = opts.particle.step - (opts.particle.prev?.step ?? 0);
3
- const nextStepDifference = (opts.particle.next?.step ?? 0) - opts.particle.step;
4
- const needWait = opts.particle.next &&
5
- opts.particle.next.step > opts.particle.step &&
6
- nextStepDifference <= opts.distance;
7
- const needSpeed = opts.particle.prev &&
8
- opts.particle.prev.step !== 0 &&
9
- opts.particle.prev.step < opts.particle.step &&
10
- prevStepDifference < opts.distance;
11
- if (opts.particle.step === 0 && needWait) {
12
- opts.particle.x = undefined;
13
- opts.particle.y = undefined;
14
- return;
3
+ const now = performance.now();
4
+ const particle = opts.particle;
5
+ if (particle._lastTime == undefined) {
6
+ particle._distanceTraveled = (opts.distance / opts.totalCount) * particle.index;
7
+ particle._lastTime = now;
8
+ particle._lastDistance = opts.distance;
15
9
  }
16
- const remainingSteps = opts.totalSteps - opts.particle.step;
17
- const progress = opts.particle.step / opts.totalSteps;
18
- if (remainingSteps <= 0) {
19
- opts.particle.x = opts.xEnd;
20
- opts.particle.y = opts.yEnd;
21
- opts.particle.step = 0;
22
- return;
10
+ const lastDistance = particle._lastDistance ?? 0;
11
+ if (lastDistance !== opts.distance && lastDistance > 0) {
12
+ const scale = opts.distance / lastDistance;
13
+ particle._distanceTraveled = ((particle._distanceTraveled ?? 0) * scale) % opts.distance;
14
+ particle._lastDistance = opts.distance;
23
15
  }
24
- const dx = opts.xEnd - opts.xStart;
25
- const dy = opts.yEnd - opts.yStart;
26
- const newX = opts.xStart + dx * progress;
27
- const newY = opts.yStart + dy * progress;
28
- opts.particle.x = newX;
29
- opts.particle.y = newY;
30
- if (needSpeed) {
31
- opts.particle.step += prevStepDifference <= 3 ? prevStepDifference : 3;
16
+ const delta = Math.min(now - particle._lastTime, FRAME_INTERVAL);
17
+ particle._lastTime = now;
18
+ particle._distanceTraveled =
19
+ ((particle._distanceTraveled ?? 0) + opts.speed * delta) % opts.distance;
20
+ const progress = particle._distanceTraveled / opts.distance;
21
+ if (opts.xControl !== 0 && opts.yControl !== 0) {
22
+ const t = 1 - progress;
23
+ particle.x =
24
+ t * t * opts.xStart + 2 * t * progress * opts.xControl + progress * progress * opts.xEnd;
25
+ particle.y =
26
+ t * t * opts.yStart + 2 * t * progress * opts.yControl + progress * progress * opts.yEnd;
32
27
  }
33
28
  else {
34
- opts.particle.step++;
29
+ const dx = opts.xEnd - opts.xStart;
30
+ const dy = opts.yEnd - opts.yStart;
31
+ particle.x = opts.xStart + dx * progress;
32
+ particle.y = opts.yStart + dy * progress;
35
33
  }
36
34
  }
35
+ function approximateQuadraticBezierLength(x0, y0, x1, y1, x2, y2) {
36
+ const dx01 = x1 - x0;
37
+ const dy01 = y1 - y0;
38
+ const dx12 = x2 - x1;
39
+ const dy12 = y2 - y1;
40
+ const dx02 = x2 - x0;
41
+ const dy02 = y2 - y0;
42
+ const a = Math.sqrt(dx01 * dx01 + dy01 * dy01);
43
+ const b = Math.sqrt(dx12 * dx12 + dy12 * dy12);
44
+ const c = Math.sqrt(dx02 * dx02 + dy02 * dy02);
45
+ return (a + b + c) / 2;
46
+ }
37
47
 
38
- export { getParticlePosition };
48
+ export { approximateQuadraticBezierLength, getParticlePosition };
39
49
  //# sourceMappingURL=get-particle-position.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-particle-position.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/get-particle-position.ts"],"sourcesContent":["import type { LinkParticle } from \"../../types\";\n\ntype GetParticlePositionOptions = {\n particle: LinkParticle;\n xStart: number;\n yStart: number;\n xEnd: number;\n yEnd: number;\n totalSteps: number;\n totalCount: number;\n distance: number;\n};\n\nexport function getParticlePosition(opts: GetParticlePositionOptions) {\n const prevStepDifference = opts.particle.step - (opts.particle.prev?.step ?? 0);\n const nextStepDifference = (opts.particle.next?.step ?? 0) - opts.particle.step;\n\n const needWait =\n opts.particle.next &&\n opts.particle.next.step > opts.particle.step &&\n nextStepDifference <= opts.distance;\n const needSpeed =\n opts.particle.prev &&\n opts.particle.prev.step !== 0 &&\n opts.particle.prev.step < opts.particle.step &&\n prevStepDifference < opts.distance;\n\n if (opts.particle.step === 0 && needWait) {\n opts.particle.x = undefined;\n opts.particle.y = undefined;\n\n return;\n }\n\n const remainingSteps = opts.totalSteps - opts.particle.step;\n const progress = opts.particle.step / opts.totalSteps;\n\n if (remainingSteps <= 0) {\n opts.particle.x = opts.xEnd;\n opts.particle.y = opts.yEnd;\n opts.particle.step = 0;\n\n return;\n }\n\n const dx = opts.xEnd - opts.xStart;\n const dy = opts.yEnd - opts.yStart;\n\n const newX = opts.xStart + dx * progress;\n const newY = opts.yStart + dy * progress;\n\n opts.particle.x = newX;\n opts.particle.y = newY;\n\n if (needSpeed) {\n opts.particle.step += prevStepDifference <= 3 ? prevStepDifference : 3;\n } else {\n opts.particle.step++;\n }\n}\n"],"names":[],"mappings":"AAaM,SAAU,mBAAmB,CAAC,IAAgC,EAAA;AAClE,IAAA,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC;AAC/E,IAAA,MAAM,kBAAkB,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI;AAE/E,IAAA,MAAM,QAAQ,GACZ,IAAI,CAAC,QAAQ,CAAC,IAAI;QAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI;AAC5C,QAAA,kBAAkB,IAAI,IAAI,CAAC,QAAQ;AACrC,IAAA,MAAM,SAAS,GACb,IAAI,CAAC,QAAQ,CAAC,IAAI;AAClB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI;AAC5C,QAAA,kBAAkB,GAAG,IAAI,CAAC,QAAQ;IAEpC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,QAAQ,EAAE;AACxC,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS;AAC3B,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS;QAE3B;;IAGF,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU;AAErD,IAAA,IAAI,cAAc,IAAI,CAAC,EAAE;QACvB,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI;QAC3B,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI;AAC3B,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;QAEtB;;IAGF,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM;IAClC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM;IAElC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ;AAExC,IAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;AACtB,IAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAEtB,IAAI,SAAS,EAAE;AACb,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,kBAAkB,IAAI,CAAC,GAAG,kBAAkB,GAAG,CAAC;;SACjE;AACL,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;;AAExB;;;;"}
1
+ {"version":3,"file":"get-particle-position.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/get-particle-position.ts"],"sourcesContent":["import type { LinkParticle } from \"../../types\";\n\ntype GetParticlePositionOptions = {\n particle: LinkParticle;\n xStart: number;\n yStart: number;\n xEnd: number;\n yEnd: number;\n xControl: number;\n yControl: number;\n distance: number;\n totalCount: number;\n speed: number;\n};\n\nconst FRAME_INTERVAL = 1000 / 60;\n\nexport function getParticlePosition(opts: GetParticlePositionOptions) {\n const now = performance.now();\n const particle = opts.particle;\n\n if (particle._lastTime == undefined) {\n particle._distanceTraveled = (opts.distance / opts.totalCount) * particle.index;\n particle._lastTime = now;\n particle._lastDistance = opts.distance;\n }\n\n const lastDistance = particle._lastDistance ?? 0;\n if (lastDistance !== opts.distance && lastDistance > 0) {\n const scale = opts.distance / lastDistance;\n particle._distanceTraveled = ((particle._distanceTraveled ?? 0) * scale) % opts.distance;\n particle._lastDistance = opts.distance;\n }\n\n const delta = Math.min(now - particle._lastTime, FRAME_INTERVAL);\n particle._lastTime = now;\n particle._distanceTraveled =\n ((particle._distanceTraveled ?? 0) + opts.speed * delta) % opts.distance;\n\n const progress = particle._distanceTraveled / opts.distance;\n if (opts.xControl !== 0 && opts.yControl !== 0) {\n const t = 1 - progress;\n particle.x =\n t * t * opts.xStart + 2 * t * progress * opts.xControl + progress * progress * opts.xEnd;\n particle.y =\n t * t * opts.yStart + 2 * t * progress * opts.yControl + progress * progress * opts.yEnd;\n } else {\n const dx = opts.xEnd - opts.xStart;\n const dy = opts.yEnd - opts.yStart;\n particle.x = opts.xStart + dx * progress;\n particle.y = opts.yStart + dy * progress;\n }\n}\n\nexport function approximateQuadraticBezierLength(\n x0: number,\n y0: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n): number {\n const dx01 = x1 - x0;\n const dy01 = y1 - y0;\n const dx12 = x2 - x1;\n const dy12 = y2 - y1;\n const dx02 = x2 - x0;\n const dy02 = y2 - y0;\n const a = Math.sqrt(dx01 * dx01 + dy01 * dy01);\n const b = Math.sqrt(dx12 * dx12 + dy12 * dy12);\n const c = Math.sqrt(dx02 * dx02 + dy02 * dy02);\n\n return (a + b + c) / 2;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ function isEmptyObject(obj) {
2
+ for (const key in obj) {
3
+ if (Object.hasOwn(obj, key)) {
4
+ return false;
5
+ }
6
+ }
7
+ return true;
8
+ }
9
+
10
+ export { isEmptyObject };
11
+ //# sourceMappingURL=is-empty-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-empty-object.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/is-empty-object.ts"],"sourcesContent":["export function isEmptyObject(obj: Record<string, unknown>) {\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n return false;\n }\n }\n return true;\n}\n"],"names":[],"mappings":"AAAM,SAAU,aAAa,CAAC,GAA4B,EAAA;AACxD,IAAA,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE;QACrB,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;AAC3B,YAAA,OAAO,KAAK;;;AAGhB,IAAA,OAAO,IAAI;AACb;;;;"}
@@ -1,6 +1,9 @@
1
1
  import { COMMON_SETTINGS } from '../../constants/common-settings.js';
2
- import '@krainovsd/js-helpers';
3
- import 'd3-array';
2
+ import '../../constants/force-settings.js';
3
+ import '../../constants/graph-settings.js';
4
+ import '../../constants/highlight-settings.js';
5
+ import '../../constants/link-settings.js';
6
+ import '../../constants/node-settings.js';
4
7
 
5
8
  const ADDITIONAL_VIEWPORT = 10;
6
9
  function isNodeVisible(opts) {
@@ -1 +1 @@
1
- {"version":3,"file":"is-node-visible.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/is-node-visible.ts"],"sourcesContent":["import type { ZoomTransform } from \"d3-zoom\";\nimport { COMMON_SETTINGS } from \"../../constants\";\nimport type { NodeInterface } from \"../../types\";\n\ntype IsNodeVisibleOptions<NodeData extends Record<string, unknown>> = {\n width: number;\n height: number;\n transform: ZoomTransform;\n node: NodeInterface<NodeData>;\n};\n\nconst ADDITIONAL_VIEWPORT = 10;\n\nexport function isNodeVisible<NodeData extends Record<string, unknown>>(\n opts: IsNodeVisibleOptions<NodeData>,\n) {\n const left = -opts.transform.x / opts.transform.k;\n const right = (opts.width - opts.transform.x) / opts.transform.k;\n const top = -opts.transform.y / opts.transform.k;\n const bottom = (opts.height - opts.transform.y) / opts.transform.k;\n\n if (opts.node.x == undefined || opts.node.y == undefined) return false;\n\n switch (opts.node._shape) {\n case \"circle\": {\n const radius = opts.node._radius ?? COMMON_SETTINGS.nodeRadius;\n\n return (\n left < opts.node.x + radius + ADDITIONAL_VIEWPORT &&\n opts.node.x - radius - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + radius + ADDITIONAL_VIEWPORT &&\n opts.node.y - radius - ADDITIONAL_VIEWPORT < bottom\n );\n }\n case \"square\":\n case \"text\": {\n const width = opts.node._width ?? COMMON_SETTINGS.nodeSize;\n const height = opts.node._height ?? COMMON_SETTINGS.nodeSize;\n\n return (\n left < opts.node.x + width + ADDITIONAL_VIEWPORT &&\n opts.node.x - width - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + height + ADDITIONAL_VIEWPORT &&\n opts.node.y - height - ADDITIONAL_VIEWPORT < bottom\n );\n }\n default: {\n const radius = opts.node._radius ?? COMMON_SETTINGS.nodeRadius;\n\n return (\n left < opts.node.x + radius + ADDITIONAL_VIEWPORT &&\n opts.node.x - radius - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + radius + ADDITIONAL_VIEWPORT &&\n opts.node.y - radius - ADDITIONAL_VIEWPORT < bottom\n );\n }\n }\n}\n"],"names":[],"mappings":";;;;AAWA,MAAM,mBAAmB,GAAG,EAAE;AAExB,SAAU,aAAa,CAC3B,IAAoC,EAAA;AAEpC,IAAA,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AACjD,IAAA,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AAChE,IAAA,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AAChD,IAAA,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AAElE,IAAA,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,SAAS;AAAE,QAAA,OAAO,KAAK;AAEtE,IAAA,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM;QACtB,KAAK,QAAQ,EAAE;YACb,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,UAAU;YAE9D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBACjD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,KAAK;gBAClD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;AAGvD,QAAA,KAAK,QAAQ;QACb,KAAK,MAAM,EAAE;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ;YAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,QAAQ;YAE5D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,mBAAmB,GAAG,KAAK;gBACjD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;QAGvD,SAAS;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,UAAU;YAE9D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBACjD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,KAAK;gBAClD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;;AAI3D;;;;"}
1
+ {"version":3,"file":"is-node-visible.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/is-node-visible.ts"],"sourcesContent":["import type { ZoomTransform } from \"d3-zoom\";\nimport { COMMON_SETTINGS } from \"../../constants\";\nimport type { NodeInterface } from \"../../types\";\n\ntype IsNodeVisibleOptions<NodeData extends Record<string, unknown>> = {\n width: number;\n height: number;\n transform: ZoomTransform;\n node: NodeInterface<NodeData>;\n};\n\nconst ADDITIONAL_VIEWPORT = 10;\n\nexport function isNodeVisible<NodeData extends Record<string, unknown>>(\n opts: IsNodeVisibleOptions<NodeData>,\n) {\n const left = -opts.transform.x / opts.transform.k;\n const right = (opts.width - opts.transform.x) / opts.transform.k;\n const top = -opts.transform.y / opts.transform.k;\n const bottom = (opts.height - opts.transform.y) / opts.transform.k;\n\n if (opts.node.x == undefined || opts.node.y == undefined) return false;\n\n switch (opts.node._shape) {\n case \"circle\": {\n const radius = opts.node._radius ?? COMMON_SETTINGS.nodeRadius;\n\n return (\n left < opts.node.x + radius + ADDITIONAL_VIEWPORT &&\n opts.node.x - radius - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + radius + ADDITIONAL_VIEWPORT &&\n opts.node.y - radius - ADDITIONAL_VIEWPORT < bottom\n );\n }\n case \"square\":\n case \"text\": {\n const width = opts.node._width ?? COMMON_SETTINGS.nodeSize;\n const height = opts.node._height ?? COMMON_SETTINGS.nodeSize;\n\n return (\n left < opts.node.x + width + ADDITIONAL_VIEWPORT &&\n opts.node.x - width - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + height + ADDITIONAL_VIEWPORT &&\n opts.node.y - height - ADDITIONAL_VIEWPORT < bottom\n );\n }\n default: {\n const radius = opts.node._radius ?? COMMON_SETTINGS.nodeRadius;\n\n return (\n left < opts.node.x + radius + ADDITIONAL_VIEWPORT &&\n opts.node.x - radius - ADDITIONAL_VIEWPORT < right &&\n top < opts.node.y + radius + ADDITIONAL_VIEWPORT &&\n opts.node.y - radius - ADDITIONAL_VIEWPORT < bottom\n );\n }\n }\n}\n"],"names":[],"mappings":";;;;;;;AAWA,MAAM,mBAAmB,GAAG,EAAE;AAExB,SAAU,aAAa,CAC3B,IAAoC,EAAA;AAEpC,IAAA,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AACjD,IAAA,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AAChE,IAAA,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AAChD,IAAA,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AAElE,IAAA,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,SAAS;AAAE,QAAA,OAAO,KAAK;AAEtE,IAAA,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM;QACtB,KAAK,QAAQ,EAAE;YACb,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,UAAU;YAE9D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBACjD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,KAAK;gBAClD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;AAGvD,QAAA,KAAK,QAAQ;QACb,KAAK,MAAM,EAAE;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ;YAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,QAAQ;YAE5D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,mBAAmB,GAAG,KAAK;gBACjD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;QAGvD,SAAS;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,UAAU;YAE9D,QACE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBACjD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,KAAK;gBAClD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB;gBAChD,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,mBAAmB,GAAG,MAAM;;;AAI3D;;;;"}
@@ -2,31 +2,34 @@ import { isObject } from '@krainovsd/js-helpers';
2
2
  import { greatest } from 'd3-array';
3
3
  import { pointerGetter } from './pointer-getter.js';
4
4
 
5
- function linkByPointerGetter({ areaRect, areaTransform, mouseEvent, links, linkHoverExtraZone, }) {
5
+ function linkByPointerGetter({ areaRect, areaTransform, mouseEvent, links, linkHoverExtraZone, curve, }) {
6
6
  if (!areaRect)
7
7
  return undefined;
8
8
  const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
9
9
  return greatest(links, (link) => {
10
- if (isNearLink(pointerX, pointerY, link, linkHoverExtraZone))
11
- return link.index;
10
+ if (!isObject(link.source) ||
11
+ !isObject(link.target) ||
12
+ link.source.visible === false ||
13
+ link.target.visible === false)
14
+ return;
15
+ const x1 = (link._x1 ?? link.source.x);
16
+ const y1 = (link._y1 ?? link.source.y);
17
+ const x2 = (link._x2 ?? link.target.x);
18
+ const y2 = (link._y2 ?? link.target.y);
19
+ const cx = link._cx ?? 0;
20
+ const cy = link._cy ?? 0;
21
+ return curve
22
+ ? hitTestQuadratic(pointerX, pointerY, x1, y1, cx, cy, x2, y2, linkHoverExtraZone)
23
+ ? link.index
24
+ : undefined
25
+ : isNearLink(pointerX, pointerY, x1, y1, x2, y2, linkHoverExtraZone)
26
+ ? link.index
27
+ : undefined;
12
28
  });
13
29
  }
14
- function isNearLink(mouseX, mouseY, link, threshold = 2) {
15
- if (!isObject(link.source) ||
16
- !isObject(link.target) ||
17
- link.source.visible === false ||
18
- link.target.visible === false)
19
- return false;
20
- const x1 = link.source.x;
21
- const y1 = link.source.y;
22
- const x2 = link.target.x;
23
- const y2 = link.target.y;
24
- const distance = distanceToLine(mouseX, mouseY, x1, y1, x2, y2);
25
- return distance <= threshold;
26
- }
27
- function distanceToLine(x, y, x1, y1, x2, y2) {
28
- const A = x - x1;
29
- const B = y - y1;
30
+ function isNearLink(px, py, x1, y1, x2, y2, threshold = 2) {
31
+ const A = px - x1;
32
+ const B = py - y1;
30
33
  const C = x2 - x1;
31
34
  const D = y2 - y1;
32
35
  const dot = A * C + B * D;
@@ -48,9 +51,56 @@ function distanceToLine(x, y, x1, y1, x2, y2) {
48
51
  xx = x1 + param * C;
49
52
  yy = y1 + param * D;
50
53
  }
51
- const dx = x - xx;
52
- const dy = y - yy;
53
- return Math.sqrt(dx * dx + dy * dy);
54
+ const dx = px - xx;
55
+ const dy = py - yy;
56
+ return Math.sqrt(dx * dx + dy * dy) <= threshold;
57
+ }
58
+ function hitTestQuadratic(px, py, x0, y0, xc, yc, x2, y2, tol) {
59
+ const bb = bezierBBox(x0, y0, xc, yc, x2, y2);
60
+ if (px < bb.minX - tol || px > bb.maxX + tol || py < bb.minY - tol || py > bb.maxY + tol) {
61
+ return false;
62
+ }
63
+ const chord = Math.hypot(x2 - x0, y2 - y0);
64
+ const contLen = Math.hypot(xc - x0, yc - y0) + Math.hypot(x2 - xc, y2 - yc);
65
+ const approxLen = (chord + contLen) / 2;
66
+ const steps = Math.max(16, Math.ceil(approxLen / 1));
67
+ const tol2 = tol * tol;
68
+ for (let i = 0; i <= steps; i++) {
69
+ const t = i / steps;
70
+ const mt = 1 - t;
71
+ const x = mt * mt * x0 + 2 * mt * t * xc + t * t * x2;
72
+ const y = mt * mt * y0 + 2 * mt * t * yc + t * t * y2;
73
+ const dx = px - x;
74
+ const dy = py - y;
75
+ if (dx * dx + dy * dy <= tol2)
76
+ return true;
77
+ }
78
+ return false;
79
+ }
80
+ function bezierBBox(x0, y0, x1, y1, x2, y2) {
81
+ function extrema(p0, p1, p2) {
82
+ const denom = p0 - 2 * p1 + p2;
83
+ if (denom === 0)
84
+ return [];
85
+ const t = (p0 - p1) / denom;
86
+ return t > 0 && t < 1 ? [t] : [];
87
+ }
88
+ const ts = [...extrema(x0, x1, x2), ...extrema(y0, y1, y2), 0, 1];
89
+ let maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity;
90
+ for (const t of ts) {
91
+ const mt = 1 - t;
92
+ const x = mt * mt * x0 + 2 * mt * t * x1 + t * t * x2;
93
+ const y = mt * mt * y0 + 2 * mt * t * y1 + t * t * y2;
94
+ if (x < minX)
95
+ minX = x;
96
+ if (x > maxX)
97
+ maxX = x;
98
+ if (y < minY)
99
+ minY = y;
100
+ if (y > maxY)
101
+ maxY = y;
102
+ }
103
+ return { minX, minY, maxX, maxY };
54
104
  }
55
105
 
56
106
  export { linkByPointerGetter };
@@ -1 +1 @@
1
- {"version":3,"file":"link-by-pointer-getter.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/link-by-pointer-getter.ts"],"sourcesContent":["import { isObject } from \"@krainovsd/js-helpers\";\nimport { greatest } from \"d3-array\";\nimport type { ZoomTransform } from \"d3-zoom\";\nimport type { LinkInterface } from \"../../types\";\nimport { pointerGetter } from \"./pointer-getter\";\n\nexport type LinkByPointerGetterOptions<\n NodeData extends Record<string, unknown>,\n LinkData extends Record<string, unknown>,\n> = {\n mouseEvent: MouseEvent | TouchEvent;\n areaRect: DOMRect | undefined;\n areaTransform: ZoomTransform;\n links: LinkInterface<NodeData, LinkData>[];\n linkHoverExtraZone: number;\n};\n\nexport function linkByPointerGetter<\n NodeData extends Record<string, unknown>,\n LinkData extends Record<string, unknown>,\n>({\n areaRect,\n areaTransform,\n mouseEvent,\n links,\n linkHoverExtraZone,\n}: LinkByPointerGetterOptions<NodeData, LinkData>): LinkInterface<NodeData, LinkData> | undefined {\n if (!areaRect) return undefined;\n\n const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);\n\n return greatest(links, (link) => {\n if (isNearLink(pointerX, pointerY, link, linkHoverExtraZone)) return link.index;\n });\n}\n\nfunction isNearLink<\n NodeData extends Record<string, unknown>,\n LinkData extends Record<string, unknown>,\n>(mouseX: number, mouseY: number, link: LinkInterface<NodeData, LinkData>, threshold = 2) {\n if (\n !isObject(link.source) ||\n !isObject(link.target) ||\n link.source.visible === false ||\n link.target.visible === false\n )\n return false;\n\n const x1 = link.source.x as number;\n const y1 = link.source.y as number;\n const x2 = link.target.x as number;\n const y2 = link.target.y as number;\n\n const distance = distanceToLine(mouseX, mouseY, x1, y1, x2, y2);\n\n return distance <= threshold;\n}\n\nfunction distanceToLine(x: number, y: number, x1: number, y1: number, x2: number, y2: number) {\n const A = x - x1;\n const B = y - y1;\n const C = x2 - x1;\n const D = y2 - y1;\n\n const dot = A * C + B * D;\n const lenSq = C * C + D * D;\n let param = -1;\n\n if (lenSq !== 0) {\n param = dot / lenSq;\n }\n\n let xx, yy;\n\n if (param < 0) {\n xx = x1;\n yy = y1;\n } else if (param > 1) {\n xx = x2;\n yy = y2;\n } else {\n xx = x1 + param * C;\n yy = y1 + param * D;\n }\n\n const dx = x - xx;\n const dy = y - yy;\n\n return Math.sqrt(dx * dx + dy * dy);\n}\n"],"names":[],"mappings":";;;;AAiBgB,SAAA,mBAAmB,CAGjC,EACA,QAAQ,EACR,aAAa,EACb,UAAU,EACV,KAAK,EACL,kBAAkB,GAC6B,EAAA;AAC/C,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,SAAS;AAE/B,IAAA,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC;AAE/E,IAAA,OAAO,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,KAAI;QAC9B,IAAI,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK;AACjF,KAAC,CAAC;AACJ;AAEA,SAAS,UAAU,CAGjB,MAAc,EAAE,MAAc,EAAE,IAAuC,EAAE,SAAS,GAAG,CAAC,EAAA;AACtF,IAAA,IACE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;AACtB,QAAA,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;AACtB,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;AAE7B,QAAA,OAAO,KAAK;AAEd,IAAA,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAW;AAClC,IAAA,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAW;AAClC,IAAA,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAW;AAClC,IAAA,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAW;AAElC,IAAA,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAE/D,OAAO,QAAQ,IAAI,SAAS;AAC9B;AAEA,SAAS,cAAc,CAAC,CAAS,EAAE,CAAS,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;AAC1F,IAAA,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;AAChB,IAAA,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;AAChB,IAAA,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE;AACjB,IAAA,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE;IAEjB,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;AAC3B,IAAA,IAAI,KAAK,GAAG,EAAE;AAEd,IAAA,IAAI,KAAK,KAAK,CAAC,EAAE;AACf,QAAA,KAAK,GAAG,GAAG,GAAG,KAAK;;IAGrB,IAAI,EAAE,EAAE,EAAE;AAEV,IAAA,IAAI,KAAK,GAAG,CAAC,EAAE;QACb,EAAE,GAAG,EAAE;QACP,EAAE,GAAG,EAAE;;AACF,SAAA,IAAI,KAAK,GAAG,CAAC,EAAE;QACpB,EAAE,GAAG,EAAE;QACP,EAAE,GAAG,EAAE;;SACF;AACL,QAAA,EAAE,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC;AACnB,QAAA,EAAE,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC;;AAGrB,IAAA,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE;AACjB,IAAA,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE;AAEjB,IAAA,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACrC;;;;"}
1
+ {"version":3,"file":"link-by-pointer-getter.js","sources":["../../../../../../src/module/GraphCanvas/lib/utils/link-by-pointer-getter.ts"],"sourcesContent":["import { isObject } from \"@krainovsd/js-helpers\";\nimport { greatest } from \"d3-array\";\nimport type { ZoomTransform } from \"d3-zoom\";\nimport type { LinkInterface } from \"../../types\";\nimport { pointerGetter } from \"./pointer-getter\";\n\nexport type LinkByPointerGetterOptions<\n NodeData extends Record<string, unknown>,\n LinkData extends Record<string, unknown>,\n> = {\n mouseEvent: MouseEvent | TouchEvent;\n areaRect: DOMRect | undefined;\n areaTransform: ZoomTransform;\n links: LinkInterface<NodeData, LinkData>[];\n linkHoverExtraZone: number;\n curve: boolean;\n};\n\nexport function linkByPointerGetter<\n NodeData extends Record<string, unknown>,\n LinkData extends Record<string, unknown>,\n>({\n areaRect,\n areaTransform,\n mouseEvent,\n links,\n linkHoverExtraZone,\n curve,\n}: LinkByPointerGetterOptions<NodeData, LinkData>): LinkInterface<NodeData, LinkData> | undefined {\n if (!areaRect) return undefined;\n\n const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);\n\n return greatest(links, (link) => {\n if (\n !isObject(link.source) ||\n !isObject(link.target) ||\n link.source.visible === false ||\n link.target.visible === false\n )\n return;\n\n const x1 = (link._x1 ?? link.source.x) as number;\n const y1 = (link._y1 ?? link.source.y) as number;\n const x2 = (link._x2 ?? link.target.x) as number;\n const y2 = (link._y2 ?? link.target.y) as number;\n const cx = link._cx ?? 0;\n const cy = link._cy ?? 0;\n\n return curve\n ? hitTestQuadratic(pointerX, pointerY, x1, y1, cx, cy, x2, y2, linkHoverExtraZone)\n ? link.index\n : undefined\n : isNearLink(pointerX, pointerY, x1, y1, x2, y2, linkHoverExtraZone)\n ? link.index\n : undefined;\n });\n}\n\nfunction isNearLink(\n px: number,\n py: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n threshold = 2,\n) {\n const A = px - x1;\n const B = py - y1;\n const C = x2 - x1;\n const D = y2 - y1;\n\n const dot = A * C + B * D;\n const lenSq = C * C + D * D;\n let param = -1;\n\n if (lenSq !== 0) {\n param = dot / lenSq;\n }\n\n let xx, yy;\n\n if (param < 0) {\n xx = x1;\n yy = y1;\n } else if (param > 1) {\n xx = x2;\n yy = y2;\n } else {\n xx = x1 + param * C;\n yy = y1 + param * D;\n }\n\n const dx = px - xx;\n const dy = py - yy;\n\n return Math.sqrt(dx * dx + dy * dy) <= threshold;\n}\n\nfunction hitTestQuadratic(\n px: number,\n py: number,\n x0: number,\n y0: number,\n xc: number,\n yc: number,\n x2: number,\n y2: number,\n tol: number,\n): boolean {\n const bb = bezierBBox(x0, y0, xc, yc, x2, y2);\n if (px < bb.minX - tol || px > bb.maxX + tol || py < bb.minY - tol || py > bb.maxY + tol) {\n return false;\n }\n\n const chord = Math.hypot(x2 - x0, y2 - y0);\n const contLen = Math.hypot(xc - x0, yc - y0) + Math.hypot(x2 - xc, y2 - yc);\n const approxLen = (chord + contLen) / 2;\n const steps = Math.max(16, Math.ceil(approxLen / 1));\n\n const tol2 = tol * tol;\n for (let i = 0; i <= steps; i++) {\n const t = i / steps;\n const mt = 1 - t;\n const x = mt * mt * x0 + 2 * mt * t * xc + t * t * x2;\n const y = mt * mt * y0 + 2 * mt * t * yc + t * t * y2;\n const dx = px - x;\n const dy = py - y;\n if (dx * dx + dy * dy <= tol2) return true;\n }\n return false;\n}\n\nfunction bezierBBox(\n x0: number,\n y0: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n): { minX: number; maxX: number; minY: number; maxY: number } {\n function extrema(p0: number, p1: number, p2: number): number[] {\n const denom = p0 - 2 * p1 + p2;\n if (denom === 0) return [];\n const t = (p0 - p1) / denom;\n return t > 0 && t < 1 ? [t] : [];\n }\n\n const ts = [...extrema(x0, x1, x2), ...extrema(y0, y1, y2), 0, 1];\n\n let maxX = -Infinity,\n maxY = -Infinity,\n minX = Infinity,\n minY = Infinity;\n\n for (const t of ts) {\n const mt = 1 - t;\n const x = mt * mt * x0 + 2 * mt * t * x1 + t * t * x2;\n const y = mt * mt * y0 + 2 * mt * t * y1 + t * t * y2;\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n\n return { minX, minY, maxX, maxY };\n}\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,17 @@
1
+ import '../constants/force-settings.js';
2
+ import '../constants/graph-settings.js';
3
+ import '../constants/highlight-settings.js';
4
+ import '../constants/link-settings.js';
5
+ import '../constants/node-settings.js';
1
6
  import '@krainovsd/js-helpers';
2
7
  import 'd3-array';
3
8
  import { calculateLinkPositionByNode } from '../lib/utils/calculate-link-position-by-node.js';
4
- import { getParticlePosition } from '../lib/utils/get-particle-position.js';
9
+ import { getParticlePosition, approximateQuadraticBezierLength } from '../lib/utils/get-particle-position.js';
5
10
  import { linkFade, linkHighlight } from '../lib/utils/link-highlight.js';
11
+ import { calculateCurveLinkPositionByNode } from '../lib/utils/calculate-curve-link-position-by-node.js';
6
12
 
7
13
  function getDrawLink() {
8
- return function drawLink(link) {
14
+ return function drawLink(link, index) {
9
15
  if (!this.context ||
10
16
  typeof link.source !== "object" ||
11
17
  typeof link.target !== "object" ||
@@ -20,8 +26,7 @@ function getDrawLink() {
20
26
  return;
21
27
  if (!link.source._visible && !link.target._visible)
22
28
  return;
23
- const id = `${link.target.id}${link.source.id}`;
24
- const linkOptions = this.linkOptionsCache[id];
29
+ const linkOptions = this.linkOptionsCache[index];
25
30
  if (!linkOptions)
26
31
  return;
27
32
  if (linkOptions.drawLink) {
@@ -36,10 +41,12 @@ function getDrawLink() {
36
41
  let arrowSize = linkOptions.arrowSize;
37
42
  let arrowBorderWidth = linkOptions.arrowBorderWidth;
38
43
  let arrowBorderColor = linkOptions.arrowBorderColor;
44
+ const currentNodeHighlighted = this.highlightedNode &&
45
+ (this.highlightedNode.id == link.source.id || this.highlightedNode.id == link.target.id);
39
46
  /** Highlight */
40
47
  if (this.highlightedNeighbors && this.highlightedNode) {
41
48
  /** By Node Not Highlight */
42
- if (this.highlightedNode.id != link.source.id && this.highlightedNode.id != link.target.id) {
49
+ if (!currentNodeHighlighted) {
43
50
  const fadeOptions = linkFade({
44
51
  arrow: this.linkSettings.arrow,
45
52
  arrowByHighlight: this.linkSettings.arrowByHighlight,
@@ -73,9 +80,10 @@ function getDrawLink() {
73
80
  arrowBorderColor = highlightOptions.arrowBorderColor;
74
81
  }
75
82
  }
83
+ const currentLinkHighlighted = this.highlightedLink === link;
76
84
  if (this.highlightedNeighbors && this.highlightedLink) {
77
85
  /** By Link Not Highlight */
78
- if (this.highlightedLink !== link) {
86
+ if (!currentLinkHighlighted) {
79
87
  const fadeOptions = linkFade({
80
88
  arrow: this.linkSettings.arrow,
81
89
  arrowByHighlight: this.linkSettings.arrowByHighlight,
@@ -119,99 +127,129 @@ function getDrawLink() {
119
127
  let yStart = link.source.y;
120
128
  let xEnd = link.target.x;
121
129
  let yEnd = link.target.y;
130
+ let xControl = 0;
131
+ let yControl = 0;
132
+ let xEndArrow = xEnd;
133
+ let yEndArrow = yEnd;
122
134
  let linkDistance = 0;
123
- if (this.linkSettings.prettyDraw ||
124
- this.linkSettings.particleFlexSpeed ||
125
- (this.linkSettings.arrow && arrowAlpha > 0)) {
126
- const isHasArrow = this.linkSettings.arrow && arrowAlpha > 0;
127
- const position = calculateLinkPositionByNode(link, isHasArrow ? arrowSize : 0);
128
- if (position) {
129
- xStart = position.x1;
130
- xEnd = position.x2;
131
- yStart = position.y1;
132
- yEnd = position.y2;
135
+ const isHasArrow = this.linkSettings.arrow && arrowAlpha > 0;
136
+ if (!this.linkSettings.curve) {
137
+ if (this.linkSettings.prettyDraw ||
138
+ (this.linkSettings.arrow && arrowAlpha > 0) ||
139
+ (this.particles && (currentNodeHighlighted || currentLinkHighlighted))) {
140
+ const position = calculateLinkPositionByNode(xStart, yStart, xEnd, yEnd, link.source, link.target, isHasArrow ? arrowSize : 0);
141
+ xStart = position.xStart;
142
+ xEnd = position.xEnd;
143
+ yStart = position.yStart;
144
+ yEnd = position.yEnd;
145
+ xEndArrow = position.xEndArrow;
146
+ yEndArrow = position.yEndArrow;
133
147
  linkDistance = position.distance;
134
148
  }
149
+ link._x1 = xStart;
150
+ link._y1 = yStart;
151
+ link._x2 = xEnd;
152
+ link._y2 = yEnd;
153
+ link._ax = xEndArrow;
154
+ link._ay = yEndArrow;
155
+ this.context.moveTo(xStart, yStart);
156
+ this.context.lineTo(xEnd, yEnd);
157
+ this.context.stroke();
158
+ }
159
+ else {
160
+ const position = calculateCurveLinkPositionByNode(xStart, yStart, xEnd, yEnd, link.source, link.target, link._groupIndex ?? 0, isHasArrow ? arrowSize : 0);
161
+ xStart = position.xStart;
162
+ yStart = position.yStart;
163
+ xEnd = position.xEnd;
164
+ yEnd = position.yEnd;
165
+ xControl = position.xControl;
166
+ yControl = position.yControl;
167
+ xEndArrow = position.xEndArrow;
168
+ yEndArrow = position.yEndArrow;
169
+ link._x1 = position.xStart;
170
+ link._y1 = position.yStart;
171
+ link._x2 = position.xEnd;
172
+ link._y2 = position.yEnd;
173
+ link._cx = position.xControl;
174
+ link._cy = position.yControl;
175
+ link._ax = position.xEndArrow;
176
+ link._ay = position.yEndArrow;
177
+ linkDistance = approximateQuadraticBezierLength(position.xStart, position.yStart, position.xControl, position.yControl, position.xEnd, position.yEnd);
178
+ this.context.beginPath();
179
+ this.context.moveTo(position.xStart, position.yStart);
180
+ this.context.quadraticCurveTo(position.xControl, position.yControl, position.xEnd, position.yEnd);
181
+ this.context.setLineDash([]);
182
+ // if (isDashed) {
183
+ // this.context.setLineDash([10, 5]);
184
+ // }
185
+ this.context.stroke();
135
186
  }
136
- this.context.moveTo(xStart, yStart);
137
- this.context.lineTo(xEnd, yEnd);
138
- this.context.stroke();
139
187
  /** Particle */
140
- if (this.linkSettings.particles &&
141
- ((this.highlightedNode &&
142
- (this.highlightedNode.id === link.source.id ||
143
- this.highlightedNode.id === link.target.id)) ||
144
- (this.highlightedLink && this.highlightedLink === link))) {
145
- const particleSteps = this.linkSettings.particleFlexSpeed
146
- ? linkDistance <= 0
147
- ? 0
148
- : linkDistance * this.linkSettings.particleFlexSpeedCoefficient
149
- : linkOptions.particleSteps;
150
- const particleCount = linkOptions.particleCount;
151
- if (!this.particles[id]) {
152
- const sourceId = link.source.id;
153
- const targetId = link.target.id;
188
+ if (this.linkSettings.particles && (currentLinkHighlighted || currentNodeHighlighted)) {
189
+ const speed = (1 / this.linkSettings.particleSpeedByDistance) * (1 + 0.5);
190
+ if (!this.particles[index]) {
191
+ const particleCount = Math.max(1, Math.floor(linkDistance / this.linkSettings.particleCountByDistance));
154
192
  const particles = [];
155
- let prevParticle;
156
193
  for (let i = 0; i < particleCount; i++) {
157
194
  const particle = {
158
- step: 0,
159
- sourceId,
160
- targetId,
161
- prev: prevParticle,
162
- next: undefined,
163
195
  index: i,
164
196
  };
165
- if (prevParticle)
166
- prevParticle.next = particle;
167
197
  particles.push(particle);
168
- prevParticle = particle;
169
- }
170
- if (particles.length >= 2) {
171
- particles[0].prev = particles[particles.length - 1];
172
- particles[particles.length - 1].next = particles[0];
173
198
  }
174
- this.particles[id] = particles;
199
+ this.particles[index] = particles;
175
200
  }
176
- if (particleSteps !== 0) {
177
- this.particles[id].forEach((particle) => {
178
- if (!this.context)
179
- return;
180
- const distance = particleSteps / particleCount;
181
- getParticlePosition({
182
- distance,
183
- particle,
184
- totalSteps: particleSteps,
185
- xEnd,
186
- xStart,
187
- yEnd,
188
- yStart,
189
- });
190
- if (particle.x != undefined && particle.y != undefined) {
191
- this.context.beginPath();
192
- this.context.strokeStyle = linkOptions.particleBorderColor;
193
- this.context.lineWidth = linkOptions.particleBorderWidth;
194
- this.context.arc(particle.x, particle.y, linkOptions.particleRadius, 0, Math.PI * 2);
195
- this.context.fillStyle = linkOptions.particleColor;
196
- this.context.fill();
197
- if (linkOptions.particleBorderWidth > 0) {
198
- this.context.stroke();
199
- }
200
- }
201
+ this.particles[index].forEach((particle, _, particles) => {
202
+ if (!this.context)
203
+ return;
204
+ getParticlePosition({
205
+ distance: linkDistance,
206
+ particle,
207
+ totalCount: particles.length,
208
+ xEnd,
209
+ xStart,
210
+ yEnd,
211
+ yStart,
212
+ xControl,
213
+ yControl,
214
+ speed,
201
215
  });
202
- }
216
+ if (particle.x != undefined && particle.y != undefined) {
217
+ this.context.beginPath();
218
+ this.context.strokeStyle = linkOptions.particleBorderColor;
219
+ this.context.lineWidth = linkOptions.particleBorderWidth;
220
+ this.context.arc(particle.x, particle.y, linkOptions.particleRadius, 0, Math.PI * 2);
221
+ this.context.fillStyle = linkOptions.particleColor;
222
+ this.context.fill();
223
+ if (linkOptions.particleBorderWidth > 0) {
224
+ this.context.stroke();
225
+ }
226
+ }
227
+ });
203
228
  }
204
229
  /** Arrow */
205
230
  if (this.linkSettings.arrow && arrowAlpha > 0) {
231
+ let angle = 0;
232
+ if (!this.linkSettings.curve) {
233
+ angle = Math.atan2(yEndArrow - yStart, xEndArrow - xStart);
234
+ }
235
+ else {
236
+ const tangentX = 2 * (xEndArrow - xControl);
237
+ const tangentY = 2 * (yEndArrow - yControl);
238
+ if (Math.abs(tangentX) < 0.001 && Math.abs(tangentY) < 0.001) {
239
+ angle = Math.atan2(yEndArrow - yControl, xEndArrow - xControl);
240
+ }
241
+ else {
242
+ angle = Math.atan2(tangentY, tangentX);
243
+ }
244
+ }
206
245
  this.context.beginPath();
207
246
  this.context.globalAlpha = arrowAlpha;
208
247
  this.context.strokeStyle = arrowBorderColor;
209
248
  this.context.lineWidth = arrowBorderWidth;
210
249
  this.context.fillStyle = arrowColor;
211
- const angle = Math.atan2(yEnd - yStart, xEnd - xStart);
212
- this.context.moveTo(xEnd, yEnd);
213
- this.context.lineTo(xEnd - arrowSize * Math.cos(angle - Math.PI / 6), yEnd - arrowSize * Math.sin(angle - Math.PI / 6));
214
- this.context.lineTo(xEnd - arrowSize * Math.cos(angle + Math.PI / 6), yEnd - arrowSize * Math.sin(angle + Math.PI / 6));
250
+ this.context.moveTo(xEndArrow, yEndArrow);
251
+ this.context.lineTo(xEndArrow - arrowSize * Math.cos(angle - Math.PI / 6), yEndArrow - arrowSize * Math.sin(angle - Math.PI / 6));
252
+ this.context.lineTo(xEndArrow - arrowSize * Math.cos(angle + Math.PI / 6), yEndArrow - arrowSize * Math.sin(angle + Math.PI / 6));
215
253
  this.context.closePath();
216
254
  this.context.fill();
217
255
  if (arrowBorderWidth > 0) {