@cristianormazabal/triton-core 0.1.0 → 0.1.2
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.
- package/dist/contracts/anchors.js +20 -0
- package/dist/contracts/anchors.js.map +1 -0
- package/dist/contracts/animations.d.ts +5 -0
- package/dist/contracts/animations.d.ts.map +1 -0
- package/dist/contracts/animations.js +15 -0
- package/dist/contracts/animations.js.map +1 -0
- package/dist/contracts/crosslink.d.ts +21 -41
- package/dist/contracts/crosslink.d.ts.map +1 -1
- package/dist/contracts/crosslink.js +17 -0
- package/dist/contracts/crosslink.js.map +1 -0
- package/dist/contracts/diagram.js +24 -0
- package/dist/contracts/diagram.js.map +1 -0
- package/dist/contracts/index.d.ts +6 -3
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +21 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/overlay.js +22 -0
- package/dist/contracts/overlay.js.map +1 -0
- package/dist/contracts/primitives.js +13 -0
- package/dist/contracts/primitives.js.map +1 -0
- package/dist/contracts/renderer.js +15 -0
- package/dist/contracts/renderer.js.map +1 -0
- package/dist/contracts/result.js +26 -0
- package/dist/contracts/result.js.map +1 -0
- package/dist/contracts/routing.d.ts +4 -0
- package/dist/contracts/routing.d.ts.map +1 -1
- package/dist/contracts/routing.js +14 -0
- package/dist/contracts/routing.js.map +1 -0
- package/dist/contracts/scene.d.ts +5 -1
- package/dist/contracts/scene.d.ts.map +1 -1
- package/dist/contracts/scene.js +18 -0
- package/dist/contracts/scene.js.map +1 -0
- package/dist/contracts/theme.js +18 -0
- package/dist/contracts/theme.js.map +1 -0
- package/dist/crosslink/connectors.d.ts +66 -0
- package/dist/crosslink/connectors.d.ts.map +1 -0
- package/dist/crosslink/connectors.js +130 -0
- package/dist/crosslink/connectors.js.map +1 -0
- package/dist/crosslink/engine2.d.ts +2 -2
- package/dist/crosslink/engine2.d.ts.map +1 -1
- package/dist/crosslink/engine2.js +783 -0
- package/dist/crosslink/engine2.js.map +1 -0
- package/dist/crosslink/engine3.d.ts +2 -2
- package/dist/crosslink/engine3.d.ts.map +1 -1
- package/dist/crosslink/engine3.js +968 -0
- package/dist/crosslink/engine3.js.map +1 -0
- package/dist/crosslink/index.d.ts +2 -0
- package/dist/crosslink/index.d.ts.map +1 -1
- package/dist/crosslink/index.js +4 -0
- package/dist/crosslink/index.js.map +1 -0
- package/dist/crosslink/render.d.ts +2 -2
- package/dist/crosslink/render.d.ts.map +1 -1
- package/dist/crosslink/render.js +997 -0
- package/dist/crosslink/render.js.map +1 -0
- package/dist/crosslink/resolve.js +331 -0
- package/dist/crosslink/resolve.js.map +1 -0
- package/dist/diagrams/{c4 → mermaid/c4}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/c4/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/c4/index.js +14 -0
- package/dist/diagrams/mermaid/c4/index.js.map +1 -0
- package/dist/diagrams/{c4 → mermaid/c4}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/c4/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/c4/ir.js +2 -0
- package/dist/diagrams/mermaid/c4/ir.js.map +1 -0
- package/dist/diagrams/{c4 → mermaid/c4}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/c4/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/c4/layout.js +110 -0
- package/dist/diagrams/mermaid/c4/layout.js.map +1 -0
- package/dist/diagrams/{class → mermaid/class}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/class/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/class/index.js +14 -0
- package/dist/diagrams/mermaid/class/index.js.map +1 -0
- package/dist/diagrams/{class → mermaid/class}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/class/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/class/ir.js +2 -0
- package/dist/diagrams/mermaid/class/ir.js.map +1 -0
- package/dist/diagrams/{class → mermaid/class}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/class/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/class/layout.js +877 -0
- package/dist/diagrams/mermaid/class/layout.js.map +1 -0
- package/dist/diagrams/{er → mermaid/er}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/er/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/er/index.js +14 -0
- package/dist/diagrams/mermaid/er/index.js.map +1 -0
- package/dist/diagrams/{er → mermaid/er}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/er/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/er/ir.js +2 -0
- package/dist/diagrams/mermaid/er/ir.js.map +1 -0
- package/dist/diagrams/{er → mermaid/er}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/er/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/er/layout.js +116 -0
- package/dist/diagrams/mermaid/er/layout.js.map +1 -0
- package/dist/diagrams/{flowchart → mermaid/flowchart}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/flowchart/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/flowchart/index.js +24 -0
- package/dist/diagrams/mermaid/flowchart/index.js.map +1 -0
- package/dist/diagrams/{flowchart → mermaid/flowchart}/ir.d.ts +7 -1
- package/dist/diagrams/mermaid/flowchart/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/flowchart/ir.js +2 -0
- package/dist/diagrams/mermaid/flowchart/ir.js.map +1 -0
- package/dist/diagrams/mermaid/flowchart/layout.d.ts +5 -0
- package/dist/diagrams/mermaid/flowchart/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/flowchart/layout.js +652 -0
- package/dist/diagrams/mermaid/flowchart/layout.js.map +1 -0
- package/dist/diagrams/{gantt → mermaid/gantt}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/gantt/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gantt/index.js +89 -0
- package/dist/diagrams/mermaid/gantt/index.js.map +1 -0
- package/dist/diagrams/{gantt → mermaid/gantt}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/gantt/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gantt/ir.js +2 -0
- package/dist/diagrams/mermaid/gantt/ir.js.map +1 -0
- package/dist/diagrams/{gantt → mermaid/gantt}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/gantt/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gantt/layout.js +95 -0
- package/dist/diagrams/mermaid/gantt/layout.js.map +1 -0
- package/dist/diagrams/{gitgraph → mermaid/gitgraph}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/gitgraph/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gitgraph/index.js +58 -0
- package/dist/diagrams/mermaid/gitgraph/index.js.map +1 -0
- package/dist/diagrams/{gitgraph → mermaid/gitgraph}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/gitgraph/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gitgraph/ir.js +2 -0
- package/dist/diagrams/mermaid/gitgraph/ir.js.map +1 -0
- package/dist/diagrams/{gitgraph → mermaid/gitgraph}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/gitgraph/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/gitgraph/layout.js +89 -0
- package/dist/diagrams/mermaid/gitgraph/layout.js.map +1 -0
- package/dist/diagrams/{journey → mermaid/journey}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/journey/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/journey/index.js +14 -0
- package/dist/diagrams/mermaid/journey/index.js.map +1 -0
- package/dist/diagrams/{journey → mermaid/journey}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/journey/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/journey/ir.js +2 -0
- package/dist/diagrams/mermaid/journey/ir.js.map +1 -0
- package/dist/diagrams/{journey → mermaid/journey}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/journey/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/journey/layout.js +92 -0
- package/dist/diagrams/mermaid/journey/layout.js.map +1 -0
- package/dist/diagrams/{kanban → mermaid/kanban}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/kanban/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/kanban/index.js +14 -0
- package/dist/diagrams/mermaid/kanban/index.js.map +1 -0
- package/dist/diagrams/{kanban → mermaid/kanban}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/kanban/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/kanban/ir.js +2 -0
- package/dist/diagrams/mermaid/kanban/ir.js.map +1 -0
- package/dist/diagrams/{kanban → mermaid/kanban}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/kanban/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/kanban/layout.js +68 -0
- package/dist/diagrams/mermaid/kanban/layout.js.map +1 -0
- package/dist/diagrams/{mindmap → mermaid/mindmap}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/mindmap/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/mindmap/index.js +52 -0
- package/dist/diagrams/mermaid/mindmap/index.js.map +1 -0
- package/dist/diagrams/{mindmap → mermaid/mindmap}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/mindmap/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/mindmap/ir.js +2 -0
- package/dist/diagrams/mermaid/mindmap/ir.js.map +1 -0
- package/dist/diagrams/{mindmap → mermaid/mindmap}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/mindmap/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/mindmap/layout.js +100 -0
- package/dist/diagrams/mermaid/mindmap/layout.js.map +1 -0
- package/dist/diagrams/{pie → mermaid/pie}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/pie/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/pie/index.js +14 -0
- package/dist/diagrams/mermaid/pie/index.js.map +1 -0
- package/dist/diagrams/{pie → mermaid/pie}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/pie/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/pie/ir.js +2 -0
- package/dist/diagrams/mermaid/pie/ir.js.map +1 -0
- package/dist/diagrams/{pie → mermaid/pie}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/pie/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/pie/layout.js +84 -0
- package/dist/diagrams/mermaid/pie/layout.js.map +1 -0
- package/dist/diagrams/{quadrant → mermaid/quadrant}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/quadrant/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/quadrant/index.js +14 -0
- package/dist/diagrams/mermaid/quadrant/index.js.map +1 -0
- package/dist/diagrams/{quadrant → mermaid/quadrant}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/quadrant/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/quadrant/ir.js +2 -0
- package/dist/diagrams/mermaid/quadrant/ir.js.map +1 -0
- package/dist/diagrams/{quadrant → mermaid/quadrant}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/quadrant/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/quadrant/layout.js +83 -0
- package/dist/diagrams/mermaid/quadrant/layout.js.map +1 -0
- package/dist/diagrams/{radar → mermaid/radar}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/radar/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/radar/index.js +14 -0
- package/dist/diagrams/mermaid/radar/index.js.map +1 -0
- package/dist/diagrams/{radar → mermaid/radar}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/radar/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/radar/ir.js +2 -0
- package/dist/diagrams/mermaid/radar/ir.js.map +1 -0
- package/dist/diagrams/{radar → mermaid/radar}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/radar/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/radar/layout.js +91 -0
- package/dist/diagrams/mermaid/radar/layout.js.map +1 -0
- package/dist/diagrams/{requirement → mermaid/requirement}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/requirement/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/requirement/index.js +14 -0
- package/dist/diagrams/mermaid/requirement/index.js.map +1 -0
- package/dist/diagrams/{requirement → mermaid/requirement}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/requirement/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/requirement/ir.js +2 -0
- package/dist/diagrams/mermaid/requirement/ir.js.map +1 -0
- package/dist/diagrams/{requirement → mermaid/requirement}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/requirement/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/requirement/layout.js +79 -0
- package/dist/diagrams/mermaid/requirement/layout.js.map +1 -0
- package/dist/diagrams/{sankey → mermaid/sankey}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/sankey/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sankey/index.js +14 -0
- package/dist/diagrams/mermaid/sankey/index.js.map +1 -0
- package/dist/diagrams/{sankey → mermaid/sankey}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/sankey/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sankey/ir.js +2 -0
- package/dist/diagrams/mermaid/sankey/ir.js.map +1 -0
- package/dist/diagrams/{sankey → mermaid/sankey}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/sankey/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sankey/layout.js +124 -0
- package/dist/diagrams/mermaid/sankey/layout.js.map +1 -0
- package/dist/diagrams/{sequence → mermaid/sequence}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/sequence/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sequence/index.js +14 -0
- package/dist/diagrams/mermaid/sequence/index.js.map +1 -0
- package/dist/diagrams/{sequence → mermaid/sequence}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/sequence/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sequence/ir.js +2 -0
- package/dist/diagrams/mermaid/sequence/ir.js.map +1 -0
- package/dist/diagrams/{sequence → mermaid/sequence}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/sequence/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/sequence/layout.js +168 -0
- package/dist/diagrams/mermaid/sequence/layout.js.map +1 -0
- package/dist/diagrams/{state → mermaid/state}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/state/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/state/index.js +14 -0
- package/dist/diagrams/mermaid/state/index.js.map +1 -0
- package/dist/diagrams/{state → mermaid/state}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/state/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/state/ir.js +2 -0
- package/dist/diagrams/mermaid/state/ir.js.map +1 -0
- package/dist/diagrams/{state → mermaid/state}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/state/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/state/layout.js +172 -0
- package/dist/diagrams/mermaid/state/layout.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/timeline/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/index.js +23 -0
- package/dist/diagrams/mermaid/timeline/index.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/timeline/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/ir.js +2 -0
- package/dist/diagrams/mermaid/timeline/ir.js.map +1 -0
- package/dist/diagrams/mermaid/timeline/layout.d.ts +5 -0
- package/dist/diagrams/mermaid/timeline/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/layout.js +228 -0
- package/dist/diagrams/mermaid/timeline/layout.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/numbered.d.ts +2 -2
- package/dist/diagrams/mermaid/timeline/numbered.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/numbered.js +112 -0
- package/dist/diagrams/mermaid/timeline/numbered.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/roadmap.d.ts +2 -2
- package/dist/diagrams/mermaid/timeline/roadmap.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/roadmap.js +125 -0
- package/dist/diagrams/mermaid/timeline/roadmap.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/serpentine.d.ts +2 -2
- package/dist/diagrams/mermaid/timeline/serpentine.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/serpentine.js +152 -0
- package/dist/diagrams/mermaid/timeline/serpentine.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/shared.d.ts +1 -1
- package/dist/diagrams/mermaid/timeline/shared.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/shared.js +47 -0
- package/dist/diagrams/mermaid/timeline/shared.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/timeline-columns.d.ts +2 -2
- package/dist/diagrams/mermaid/timeline/timeline-columns.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/timeline-columns.js +136 -0
- package/dist/diagrams/mermaid/timeline/timeline-columns.js.map +1 -0
- package/dist/diagrams/{timeline → mermaid/timeline}/vertical-spine.d.ts +2 -2
- package/dist/diagrams/mermaid/timeline/vertical-spine.d.ts.map +1 -0
- package/dist/diagrams/mermaid/timeline/vertical-spine.js +136 -0
- package/dist/diagrams/mermaid/timeline/vertical-spine.js.map +1 -0
- package/dist/diagrams/{xychart → mermaid/xychart}/index.d.ts +1 -1
- package/dist/diagrams/mermaid/xychart/index.d.ts.map +1 -0
- package/dist/diagrams/mermaid/xychart/index.js +14 -0
- package/dist/diagrams/mermaid/xychart/index.js.map +1 -0
- package/dist/diagrams/{xychart → mermaid/xychart}/ir.d.ts +1 -1
- package/dist/diagrams/mermaid/xychart/ir.d.ts.map +1 -0
- package/dist/diagrams/mermaid/xychart/ir.js +2 -0
- package/dist/diagrams/mermaid/xychart/ir.js.map +1 -0
- package/dist/diagrams/{xychart → mermaid/xychart}/layout.d.ts +2 -2
- package/dist/diagrams/mermaid/xychart/layout.d.ts.map +1 -0
- package/dist/diagrams/mermaid/xychart/layout.js +120 -0
- package/dist/diagrams/mermaid/xychart/layout.js.map +1 -0
- package/dist/diagrams/{architecture → triton/architecture}/index.d.ts +1 -1
- package/dist/diagrams/triton/architecture/index.d.ts.map +1 -0
- package/dist/diagrams/triton/architecture/index.js +14 -0
- package/dist/diagrams/triton/architecture/index.js.map +1 -0
- package/dist/diagrams/{architecture → triton/architecture}/ir.d.ts +1 -1
- package/dist/diagrams/triton/architecture/ir.d.ts.map +1 -0
- package/dist/diagrams/triton/architecture/ir.js +2 -0
- package/dist/diagrams/triton/architecture/ir.js.map +1 -0
- package/dist/diagrams/{architecture → triton/architecture}/layout.d.ts +2 -2
- package/dist/diagrams/triton/architecture/layout.d.ts.map +1 -0
- package/dist/diagrams/triton/architecture/layout.js +124 -0
- package/dist/diagrams/triton/architecture/layout.js.map +1 -0
- package/dist/diagrams/{block → triton/block}/index.d.ts +1 -1
- package/dist/diagrams/triton/block/index.d.ts.map +1 -0
- package/dist/diagrams/triton/block/index.js +14 -0
- package/dist/diagrams/triton/block/index.js.map +1 -0
- package/dist/diagrams/{block → triton/block}/ir.d.ts +1 -1
- package/dist/diagrams/triton/block/ir.d.ts.map +1 -0
- package/dist/diagrams/triton/block/ir.js +2 -0
- package/dist/diagrams/triton/block/ir.js.map +1 -0
- package/dist/diagrams/{block → triton/block}/layout.d.ts +2 -2
- package/dist/diagrams/triton/block/layout.d.ts.map +1 -0
- package/dist/diagrams/triton/block/layout.js +73 -0
- package/dist/diagrams/triton/block/layout.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/graph/graph.d.ts +1 -1
- package/dist/diagrams/triton/ds/graph/graph.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/graph/graph.js +140 -0
- package/dist/diagrams/triton/ds/graph/graph.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/hashmap/hashmap.d.ts +1 -1
- package/dist/diagrams/triton/ds/hashmap/hashmap.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/hashmap/hashmap.js +131 -0
- package/dist/diagrams/triton/ds/hashmap/hashmap.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/matrix/matrix.d.ts +1 -1
- package/dist/diagrams/triton/ds/matrix/matrix.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/matrix/matrix.js +113 -0
- package/dist/diagrams/triton/ds/matrix/matrix.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/queue/cqueue.d.ts +5 -3
- package/dist/diagrams/triton/ds/queue/cqueue.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/queue/cqueue.js +195 -0
- package/dist/diagrams/triton/ds/queue/cqueue.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/queue/deque.d.ts +3 -1
- package/dist/diagrams/triton/ds/queue/deque.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/queue/deque.js +118 -0
- package/dist/diagrams/triton/ds/queue/deque.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/queue/pqueue.d.ts +5 -3
- package/dist/diagrams/triton/ds/queue/pqueue.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/queue/pqueue.js +129 -0
- package/dist/diagrams/triton/ds/queue/pqueue.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/queue/queue.d.ts +3 -1
- package/dist/diagrams/triton/ds/queue/queue.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/queue/queue.js +128 -0
- package/dist/diagrams/triton/ds/queue/queue.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/queue/shared.d.ts +18 -2
- package/dist/diagrams/triton/ds/queue/shared.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/queue/shared.js +186 -0
- package/dist/diagrams/triton/ds/queue/shared.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/stack/stack.d.ts +3 -1
- package/dist/diagrams/triton/ds/stack/stack.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/stack/stack.js +119 -0
- package/dist/diagrams/triton/ds/stack/stack.js.map +1 -0
- package/dist/diagrams/triton/ds/struct/array.d.ts +52 -0
- package/dist/diagrams/triton/ds/struct/array.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/struct/array.js +491 -0
- package/dist/diagrams/triton/ds/struct/array.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/struct/linkedlist.d.ts +1 -1
- package/dist/diagrams/triton/ds/struct/linkedlist.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/struct/linkedlist.js +99 -0
- package/dist/diagrams/triton/ds/struct/linkedlist.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/struct/memory.d.ts +1 -1
- package/dist/diagrams/triton/ds/struct/memory.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/struct/memory.js +172 -0
- package/dist/diagrams/triton/ds/struct/memory.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/struct/page.d.ts +1 -1
- package/dist/diagrams/triton/ds/struct/page.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/struct/page.js +137 -0
- package/dist/diagrams/triton/ds/struct/page.js.map +1 -0
- package/dist/diagrams/triton/ds/struct/shared.d.ts +17 -0
- package/dist/diagrams/triton/ds/struct/shared.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/struct/shared.js +60 -0
- package/dist/diagrams/triton/ds/struct/shared.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/avl.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/avl.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/avl.js +87 -0
- package/dist/diagrams/triton/ds/tree/avl.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/btree.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/btree.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/btree.js +76 -0
- package/dist/diagrams/triton/ds/tree/btree.js.map +1 -0
- package/dist/diagrams/triton/ds/tree/build.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/build.js +51 -0
- package/dist/diagrams/triton/ds/tree/build.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/heap.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/heap.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/heap.js +48 -0
- package/dist/diagrams/triton/ds/tree/heap.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/index.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/index.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/index.js +20 -0
- package/dist/diagrams/triton/ds/tree/index.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/ir.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/ir.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/ir.js +10 -0
- package/dist/diagrams/triton/ds/tree/ir.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/layout.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/layout.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/layout.js +281 -0
- package/dist/diagrams/triton/ds/tree/layout.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/plan.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/plan.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/plan.js +48 -0
- package/dist/diagrams/triton/ds/tree/plan.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/radix.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/radix.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/radix.js +88 -0
- package/dist/diagrams/triton/ds/tree/radix.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/rbtree.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/rbtree.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/rbtree.js +78 -0
- package/dist/diagrams/triton/ds/tree/rbtree.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/tree/segtree.d.ts +1 -1
- package/dist/diagrams/triton/ds/tree/segtree.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/tree/segtree.js +51 -0
- package/dist/diagrams/triton/ds/tree/segtree.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/trie/trie.d.ts +1 -1
- package/dist/diagrams/triton/ds/trie/trie.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/trie/trie.js +58 -0
- package/dist/diagrams/triton/ds/trie/trie.js.map +1 -0
- package/dist/diagrams/{ds → triton/ds}/unionfind/unionfind.d.ts +1 -1
- package/dist/diagrams/triton/ds/unionfind/unionfind.d.ts.map +1 -0
- package/dist/diagrams/triton/ds/unionfind/unionfind.js +110 -0
- package/dist/diagrams/triton/ds/unionfind/unionfind.js.map +1 -0
- package/dist/diagrams/{packet → triton/packet}/index.d.ts +1 -1
- package/dist/diagrams/triton/packet/index.d.ts.map +1 -0
- package/dist/diagrams/triton/packet/index.js +14 -0
- package/dist/diagrams/triton/packet/index.js.map +1 -0
- package/dist/diagrams/{packet → triton/packet}/ir.d.ts +1 -1
- package/dist/diagrams/triton/packet/ir.d.ts.map +1 -0
- package/dist/diagrams/triton/packet/ir.js +2 -0
- package/dist/diagrams/triton/packet/ir.js.map +1 -0
- package/dist/diagrams/{packet → triton/packet}/layout.d.ts +2 -2
- package/dist/diagrams/triton/packet/layout.d.ts.map +1 -0
- package/dist/diagrams/triton/packet/layout.js +69 -0
- package/dist/diagrams/triton/packet/layout.js.map +1 -0
- package/dist/diagrams/{poster → triton/poster}/index.d.ts +1 -1
- package/dist/diagrams/triton/poster/index.d.ts.map +1 -0
- package/dist/diagrams/triton/poster/index.js +127 -0
- package/dist/diagrams/triton/poster/index.js.map +1 -0
- package/dist/diagrams/{poster → triton/poster}/ir.d.ts +1 -3
- package/dist/diagrams/{poster → triton/poster}/ir.d.ts.map +1 -1
- package/dist/diagrams/triton/poster/ir.js +2 -0
- package/dist/diagrams/triton/poster/ir.js.map +1 -0
- package/dist/diagrams/{poster → triton/poster}/layout.d.ts +2 -2
- package/dist/diagrams/triton/poster/layout.d.ts.map +1 -0
- package/dist/diagrams/triton/poster/layout.js +517 -0
- package/dist/diagrams/triton/poster/layout.js.map +1 -0
- package/dist/diagrams/{topology → triton/topology}/topology.d.ts +2 -2
- package/dist/diagrams/triton/topology/topology.d.ts.map +1 -0
- package/dist/diagrams/triton/topology/topology.js +198 -0
- package/dist/diagrams/triton/topology/topology.js.map +1 -0
- package/dist/frontend/detect.js +94 -0
- package/dist/frontend/detect.js.map +1 -0
- package/dist/frontend/index.d.ts +4 -4
- package/dist/frontend/index.d.ts.map +1 -1
- package/dist/frontend/index.js +196 -0
- package/dist/frontend/index.js.map +1 -0
- package/dist/frontend/preprocess.d.ts +28 -0
- package/dist/frontend/preprocess.d.ts.map +1 -0
- package/dist/frontend/preprocess.js +60 -0
- package/dist/frontend/preprocess.js.map +1 -0
- package/dist/frontend/registry.js +25 -0
- package/dist/frontend/registry.js.map +1 -0
- package/dist/graph/connect.js +44 -0
- package/dist/graph/connect.js.map +1 -0
- package/dist/graph/layered.d.ts +65 -5
- package/dist/graph/layered.d.ts.map +1 -1
- package/dist/graph/layered.js +936 -0
- package/dist/graph/layered.js.map +1 -0
- package/dist/graph/tree.js +107 -0
- package/dist/graph/tree.js.map +1 -0
- package/dist/index.js +4691 -2735
- package/dist/index.js.map +4 -4
- package/dist/overlay/apply.js +22 -0
- package/dist/overlay/apply.js.map +1 -0
- package/dist/overlay/compiler.js +48 -0
- package/dist/overlay/compiler.js.map +1 -0
- package/dist/overlay/layout.js +242 -0
- package/dist/overlay/layout.js.map +1 -0
- package/dist/palette/categorical.js +21 -0
- package/dist/palette/categorical.js.map +1 -0
- package/dist/render/registry.js +22 -0
- package/dist/render/registry.js.map +1 -0
- package/dist/render/svg.d.ts.map +1 -1
- package/dist/render/svg.js +287 -0
- package/dist/render/svg.js.map +1 -0
- package/dist/routing/d3-curves.d.ts +31 -0
- package/dist/routing/d3-curves.d.ts.map +1 -0
- package/dist/routing/d3-curves.js +58 -0
- package/dist/routing/d3-curves.js.map +1 -0
- package/dist/routing/registry.js +22 -0
- package/dist/routing/registry.js.map +1 -0
- package/dist/routing/router.d.ts +3 -0
- package/dist/routing/router.d.ts.map +1 -1
- package/dist/routing/router.js +849 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/scene/build.d.ts +3 -1
- package/dist/scene/build.d.ts.map +1 -1
- package/dist/scene/build.js +57 -0
- package/dist/scene/build.js.map +1 -0
- package/dist/scene/strip.js +46 -0
- package/dist/scene/strip.js.map +1 -0
- package/dist/style/cost.js +39 -0
- package/dist/style/cost.js.map +1 -0
- package/dist/text/metrics.js +62 -0
- package/dist/text/metrics.js.map +1 -0
- package/dist/text/wrap.js +84 -0
- package/dist/text/wrap.js.map +1 -0
- package/dist/theme/preset.d.ts +2 -0
- package/dist/theme/preset.d.ts.map +1 -1
- package/dist/theme/preset.js +295 -0
- package/dist/theme/preset.js.map +1 -0
- package/dist/theme/resolver.js +15 -0
- package/dist/theme/resolver.js.map +1 -0
- package/dist/time/dates.js +301 -0
- package/dist/time/dates.js.map +1 -0
- package/dist/util/round.js +17 -0
- package/dist/util/round.js.map +1 -0
- package/package.json +1 -1
- package/dist/diagrams/architecture/index.d.ts.map +0 -1
- package/dist/diagrams/architecture/ir.d.ts.map +0 -1
- package/dist/diagrams/architecture/layout.d.ts.map +0 -1
- package/dist/diagrams/block/index.d.ts.map +0 -1
- package/dist/diagrams/block/ir.d.ts.map +0 -1
- package/dist/diagrams/block/layout.d.ts.map +0 -1
- package/dist/diagrams/c4/index.d.ts.map +0 -1
- package/dist/diagrams/c4/ir.d.ts.map +0 -1
- package/dist/diagrams/c4/layout.d.ts.map +0 -1
- package/dist/diagrams/class/index.d.ts.map +0 -1
- package/dist/diagrams/class/ir.d.ts.map +0 -1
- package/dist/diagrams/class/layout.d.ts.map +0 -1
- package/dist/diagrams/ds/graph/graph.d.ts.map +0 -1
- package/dist/diagrams/ds/hashmap/hashmap.d.ts.map +0 -1
- package/dist/diagrams/ds/matrix/matrix.d.ts.map +0 -1
- package/dist/diagrams/ds/queue/cqueue.d.ts.map +0 -1
- package/dist/diagrams/ds/queue/deque.d.ts.map +0 -1
- package/dist/diagrams/ds/queue/pqueue.d.ts.map +0 -1
- package/dist/diagrams/ds/queue/queue.d.ts.map +0 -1
- package/dist/diagrams/ds/queue/shared.d.ts.map +0 -1
- package/dist/diagrams/ds/stack/stack.d.ts.map +0 -1
- package/dist/diagrams/ds/struct/array.d.ts +0 -28
- package/dist/diagrams/ds/struct/array.d.ts.map +0 -1
- package/dist/diagrams/ds/struct/linkedlist.d.ts.map +0 -1
- package/dist/diagrams/ds/struct/memory.d.ts.map +0 -1
- package/dist/diagrams/ds/struct/page.d.ts.map +0 -1
- package/dist/diagrams/ds/struct/shared.d.ts +0 -9
- package/dist/diagrams/ds/struct/shared.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/avl.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/btree.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/build.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/heap.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/index.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/ir.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/layout.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/plan.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/radix.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/rbtree.d.ts.map +0 -1
- package/dist/diagrams/ds/tree/segtree.d.ts.map +0 -1
- package/dist/diagrams/ds/trie/trie.d.ts.map +0 -1
- package/dist/diagrams/ds/unionfind/unionfind.d.ts.map +0 -1
- package/dist/diagrams/er/index.d.ts.map +0 -1
- package/dist/diagrams/er/ir.d.ts.map +0 -1
- package/dist/diagrams/er/layout.d.ts.map +0 -1
- package/dist/diagrams/flowchart/index.d.ts.map +0 -1
- package/dist/diagrams/flowchart/ir.d.ts.map +0 -1
- package/dist/diagrams/flowchart/layout.d.ts +0 -5
- package/dist/diagrams/flowchart/layout.d.ts.map +0 -1
- package/dist/diagrams/gantt/index.d.ts.map +0 -1
- package/dist/diagrams/gantt/ir.d.ts.map +0 -1
- package/dist/diagrams/gantt/layout.d.ts.map +0 -1
- package/dist/diagrams/gitgraph/index.d.ts.map +0 -1
- package/dist/diagrams/gitgraph/ir.d.ts.map +0 -1
- package/dist/diagrams/gitgraph/layout.d.ts.map +0 -1
- package/dist/diagrams/journey/index.d.ts.map +0 -1
- package/dist/diagrams/journey/ir.d.ts.map +0 -1
- package/dist/diagrams/journey/layout.d.ts.map +0 -1
- package/dist/diagrams/kanban/index.d.ts.map +0 -1
- package/dist/diagrams/kanban/ir.d.ts.map +0 -1
- package/dist/diagrams/kanban/layout.d.ts.map +0 -1
- package/dist/diagrams/mindmap/index.d.ts.map +0 -1
- package/dist/diagrams/mindmap/ir.d.ts.map +0 -1
- package/dist/diagrams/mindmap/layout.d.ts.map +0 -1
- package/dist/diagrams/packet/index.d.ts.map +0 -1
- package/dist/diagrams/packet/ir.d.ts.map +0 -1
- package/dist/diagrams/packet/layout.d.ts.map +0 -1
- package/dist/diagrams/pie/index.d.ts.map +0 -1
- package/dist/diagrams/pie/ir.d.ts.map +0 -1
- package/dist/diagrams/pie/layout.d.ts.map +0 -1
- package/dist/diagrams/poster/index.d.ts.map +0 -1
- package/dist/diagrams/poster/layout.d.ts.map +0 -1
- package/dist/diagrams/quadrant/index.d.ts.map +0 -1
- package/dist/diagrams/quadrant/ir.d.ts.map +0 -1
- package/dist/diagrams/quadrant/layout.d.ts.map +0 -1
- package/dist/diagrams/radar/index.d.ts.map +0 -1
- package/dist/diagrams/radar/ir.d.ts.map +0 -1
- package/dist/diagrams/radar/layout.d.ts.map +0 -1
- package/dist/diagrams/requirement/index.d.ts.map +0 -1
- package/dist/diagrams/requirement/ir.d.ts.map +0 -1
- package/dist/diagrams/requirement/layout.d.ts.map +0 -1
- package/dist/diagrams/sankey/index.d.ts.map +0 -1
- package/dist/diagrams/sankey/ir.d.ts.map +0 -1
- package/dist/diagrams/sankey/layout.d.ts.map +0 -1
- package/dist/diagrams/sequence/index.d.ts.map +0 -1
- package/dist/diagrams/sequence/ir.d.ts.map +0 -1
- package/dist/diagrams/sequence/layout.d.ts.map +0 -1
- package/dist/diagrams/state/index.d.ts.map +0 -1
- package/dist/diagrams/state/ir.d.ts.map +0 -1
- package/dist/diagrams/state/layout.d.ts.map +0 -1
- package/dist/diagrams/timeline/index.d.ts.map +0 -1
- package/dist/diagrams/timeline/ir.d.ts.map +0 -1
- package/dist/diagrams/timeline/layout.d.ts +0 -5
- package/dist/diagrams/timeline/layout.d.ts.map +0 -1
- package/dist/diagrams/timeline/numbered.d.ts.map +0 -1
- package/dist/diagrams/timeline/roadmap.d.ts.map +0 -1
- package/dist/diagrams/timeline/serpentine.d.ts.map +0 -1
- package/dist/diagrams/timeline/shared.d.ts.map +0 -1
- package/dist/diagrams/timeline/timeline-columns.d.ts.map +0 -1
- package/dist/diagrams/timeline/vertical-spine.d.ts.map +0 -1
- package/dist/diagrams/topology/topology.d.ts.map +0 -1
- package/dist/diagrams/xychart/index.d.ts.map +0 -1
- package/dist/diagrams/xychart/ir.d.ts.map +0 -1
- package/dist/diagrams/xychart/layout.d.ts.map +0 -1
- /package/dist/diagrams/{ds → triton/ds}/tree/build.d.ts +0 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Link Rendering
|
|
3
|
+
*
|
|
4
|
+
* Converts resolved cross-links into SceneElements (paths + labels)
|
|
5
|
+
* that are layered on top of the poster's cell elements.
|
|
6
|
+
*
|
|
7
|
+
* Routes use the orthogonal router with port-direction hints from the
|
|
8
|
+
* resolved link's fromSide/toSide.
|
|
9
|
+
*/
|
|
10
|
+
import { isRenderedConnectorAnimation } from '../contracts/crosslink.js';
|
|
11
|
+
import { getRouter } from '../routing/registry.js';
|
|
12
|
+
import { createRouter } from '../routing/router.js';
|
|
13
|
+
const CROSSLINK_ARROW_ID = 'triton-crosslink-arrow';
|
|
14
|
+
const CROSSLINK_ARROW_BOTH_ID = 'triton-crosslink-arrow-both';
|
|
15
|
+
/**
|
|
16
|
+
* Render resolved cross-links into scene elements.
|
|
17
|
+
*
|
|
18
|
+
* @param resolved — links with resolved port positions in poster space
|
|
19
|
+
* @param theme — for styling (colours, fonts, stroke widths)
|
|
20
|
+
* @param anchors — full anchor registry for obstacle avoidance
|
|
21
|
+
* @param occupiedRects — bounding boxes of existing text/elements that labels must avoid
|
|
22
|
+
* @param routingObstacles — additional thin obstacles (cell borders) for the router to avoid
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Amount (px) by which intermediate-cell rectangles are shrunk on each side
|
|
26
|
+
* before being used as routing obstacles. Must match the router’s padding
|
|
27
|
+
* so that effective corridor width (gap + 2×SHRINK − 2×padding) leaves room.
|
|
28
|
+
*/
|
|
29
|
+
const CELL_SHRINK = 12;
|
|
30
|
+
export function renderCrossLinks(resolved, theme, anchors, occupiedRects, routingObstacles, cellRects) {
|
|
31
|
+
const { palette, typography, edges: edgeTheme } = theme;
|
|
32
|
+
const elements = [];
|
|
33
|
+
const defs = [];
|
|
34
|
+
let needsArrow = false;
|
|
35
|
+
let needsBiArrow = false;
|
|
36
|
+
// Extract obstacles from the anchor registry (all node bounds)
|
|
37
|
+
// Note: cell borders are handled separately in post-route nudging, not as generic obstacles
|
|
38
|
+
const obstacles = [];
|
|
39
|
+
if (anchors) {
|
|
40
|
+
for (const anchor of Object.values(anchors)) {
|
|
41
|
+
obstacles.push(anchor.bounds);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Assign distinct colours to explicit links
|
|
45
|
+
const categoricalPalette = [
|
|
46
|
+
'#E11D48', '#16A34A', '#9333EA', '#0891B2',
|
|
47
|
+
'#CA8A04', '#DC2626', '#2563EB', '#7C3AED',
|
|
48
|
+
];
|
|
49
|
+
let explicitColorIdx = 0;
|
|
50
|
+
// Collect labels for de-collision pass after all routes are computed
|
|
51
|
+
const pendingLabels = [];
|
|
52
|
+
// Phase 1: Compute all routes
|
|
53
|
+
const pendingRoutes = [];
|
|
54
|
+
for (const rLink of resolved) {
|
|
55
|
+
const { link, fromPort, toPort, fromSide, toSide } = rLink;
|
|
56
|
+
let color;
|
|
57
|
+
{
|
|
58
|
+
color = categoricalPalette[explicitColorIdx % categoricalPalette.length];
|
|
59
|
+
explicitColorIdx++;
|
|
60
|
+
}
|
|
61
|
+
const fromDir = sideToPortDir(fromSide);
|
|
62
|
+
const toDir = sideToPortDir(toSide);
|
|
63
|
+
const routeStyle = link.routing ?? 'orthogonal';
|
|
64
|
+
const router = getRouter(routeStyle) ?? createRouter(routeStyle);
|
|
65
|
+
const tension = link.props?.tension;
|
|
66
|
+
// Build per-link obstacles: all node bounds + shrunken intermediate-cell
|
|
67
|
+
// rects. Intermediate cells (neither source nor target) are shrunk inward
|
|
68
|
+
// by CELL_SHRINK so routes travel through inter-cell corridors instead of
|
|
69
|
+
// cutting through other cells’ content areas. Source/target cells are
|
|
70
|
+
// excluded so routes can freely exit/enter through their own cell’s space.
|
|
71
|
+
let linkObstacles = obstacles;
|
|
72
|
+
if (cellRects) {
|
|
73
|
+
const srcId = link.from.cellPath.join('.');
|
|
74
|
+
const dstId = link.to.cellPath.join('.');
|
|
75
|
+
const extra = [];
|
|
76
|
+
for (const [cellId, r] of cellRects) {
|
|
77
|
+
if (cellId === srcId || cellId === dstId)
|
|
78
|
+
continue;
|
|
79
|
+
const sw = r.width - 2 * CELL_SHRINK;
|
|
80
|
+
const sh = r.height - 2 * CELL_SHRINK;
|
|
81
|
+
if (sw > 0 && sh > 0) {
|
|
82
|
+
extra.push({ x: r.x + CELL_SHRINK, y: r.y + CELL_SHRINK, width: sw, height: sh });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (extra.length > 0)
|
|
86
|
+
linkObstacles = [...obstacles, ...extra];
|
|
87
|
+
}
|
|
88
|
+
const route = router.route({
|
|
89
|
+
from: fromPort,
|
|
90
|
+
to: toPort,
|
|
91
|
+
style: routeStyle,
|
|
92
|
+
obstacles: linkObstacles,
|
|
93
|
+
padding: 12,
|
|
94
|
+
fromDir,
|
|
95
|
+
toDir,
|
|
96
|
+
...(tension != null ? { tension } : {}),
|
|
97
|
+
});
|
|
98
|
+
const dash = edgeStyleToDash(link.style);
|
|
99
|
+
// Animation: explicit DSL value wins; default is 'march' for dashed/dotted, nothing for solid.
|
|
100
|
+
const animation = link.animation === 'none' ? undefined :
|
|
101
|
+
isRenderedConnectorAnimation(link.animation) ? link.animation :
|
|
102
|
+
dash ? 'march' : undefined;
|
|
103
|
+
let markerEnd;
|
|
104
|
+
let markerStart;
|
|
105
|
+
if (link.direction === 'directed') {
|
|
106
|
+
markerEnd = CROSSLINK_ARROW_ID;
|
|
107
|
+
needsArrow = true;
|
|
108
|
+
}
|
|
109
|
+
else if (link.direction === 'bidirectional') {
|
|
110
|
+
markerEnd = CROSSLINK_ARROW_ID;
|
|
111
|
+
markerStart = CROSSLINK_ARROW_BOTH_ID;
|
|
112
|
+
needsArrow = true;
|
|
113
|
+
needsBiArrow = true;
|
|
114
|
+
}
|
|
115
|
+
pendingRoutes.push({
|
|
116
|
+
points: route.points,
|
|
117
|
+
routePath: routeStyle !== 'orthogonal' ? route.path : undefined,
|
|
118
|
+
routing: routeStyle,
|
|
119
|
+
fromDir,
|
|
120
|
+
toDir,
|
|
121
|
+
color,
|
|
122
|
+
dash,
|
|
123
|
+
animation,
|
|
124
|
+
markerEnd,
|
|
125
|
+
markerStart,
|
|
126
|
+
label: link.label,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// Phase 2: Deflect straight routes that cross other routes
|
|
130
|
+
deflectCrossingStraightRoutes(pendingRoutes, obstacles);
|
|
131
|
+
// Phase 3: Nudge segments away from cell borders (orthogonal routes only)
|
|
132
|
+
if (routingObstacles && routingObstacles.length > 0) {
|
|
133
|
+
const orthoRoutes = pendingRoutes.filter(r => r.routing === 'orthogonal');
|
|
134
|
+
nudgeOffBorders(orthoRoutes, routingObstacles);
|
|
135
|
+
}
|
|
136
|
+
// Phase 4a: Bezier separation — fan apart or fall back crossing beziers to orthogonal
|
|
137
|
+
// Runs BEFORE channel separation so any new orthogonal routes get included.
|
|
138
|
+
const CHANNEL_GAP = 12;
|
|
139
|
+
separateBezierCurves(pendingRoutes.filter(r => r.routing === 'bezier'), CHANNEL_GAP, obstacles, pendingRoutes);
|
|
140
|
+
// Phase 4b: Channel separation — offset overlapping parallel segments (all orthogonal, including fallbacks)
|
|
141
|
+
separateOverlappingChannels(pendingRoutes.filter(r => r.routing === 'orthogonal'), CHANNEL_GAP);
|
|
142
|
+
// ─── Label position staggering ───────────────────────────────────────────
|
|
143
|
+
// Routes sharing the same corridor (same dominant-segment axis + close coord)
|
|
144
|
+
// would all land their labels at the same midpoint. Distribute positions
|
|
145
|
+
// evenly along the route: 1/(n+1), 2/(n+1), …, n/(n+1).
|
|
146
|
+
const CORRIDOR_TOL = 20; // px — routes within this band share a corridor
|
|
147
|
+
const labelFractions = new Array(pendingRoutes.length).fill(0.5);
|
|
148
|
+
const labeledIndices = pendingRoutes.map((pr, i) => pr.label ? i : -1).filter(i => i >= 0);
|
|
149
|
+
const staggerAssigned = new Set();
|
|
150
|
+
for (const i of labeledIndices) {
|
|
151
|
+
if (staggerAssigned.has(i))
|
|
152
|
+
continue;
|
|
153
|
+
const infoI = dominantSegmentInfo(pendingRoutes[i].points);
|
|
154
|
+
const group = [i];
|
|
155
|
+
for (const j of labeledIndices) {
|
|
156
|
+
if (j === i || staggerAssigned.has(j))
|
|
157
|
+
continue;
|
|
158
|
+
const infoJ = dominantSegmentInfo(pendingRoutes[j].points);
|
|
159
|
+
if (infoI.axis === infoJ.axis && Math.abs(infoI.coord - infoJ.coord) < CORRIDOR_TOL) {
|
|
160
|
+
group.push(j);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (group.length > 1) {
|
|
164
|
+
group.sort((a, b) => a - b); // consistent ordering by route index
|
|
165
|
+
const n = group.length;
|
|
166
|
+
group.forEach((idx, k) => { labelFractions[idx] = (k + 1) / (n + 1); });
|
|
167
|
+
group.forEach(idx => staggerAssigned.add(idx));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Phase 5: Emit path elements and collect labels
|
|
171
|
+
for (let prIdx = 0; prIdx < pendingRoutes.length; prIdx++) {
|
|
172
|
+
const pr = pendingRoutes[prIdx];
|
|
173
|
+
// Bezier/straight routes use the router's SVG path; orthogonal rebuilds from points
|
|
174
|
+
const path = pr.routePath ?? pr.points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
|
|
175
|
+
const pathEl = {
|
|
176
|
+
type: 'path',
|
|
177
|
+
d: path,
|
|
178
|
+
stroke: pr.color,
|
|
179
|
+
strokeWidth: edgeTheme.strokeWidth + 0.5,
|
|
180
|
+
...(pr.dash ? { strokeDasharray: pr.dash } : {}),
|
|
181
|
+
...(pr.animation ? { animated: pr.animation } : {}),
|
|
182
|
+
...(pr.markerEnd ? { markerEnd: pr.markerEnd } : {}),
|
|
183
|
+
...(pr.markerStart ? { markerStart: pr.markerStart } : {}),
|
|
184
|
+
};
|
|
185
|
+
elements.push(pathEl);
|
|
186
|
+
if (pr.label) {
|
|
187
|
+
const labelPos = pointAtFraction(pr.points, labelFractions[prIdx] ?? 0.5);
|
|
188
|
+
pendingLabels.push({
|
|
189
|
+
content: pr.label,
|
|
190
|
+
x: labelPos.x,
|
|
191
|
+
y: labelPos.y - 6,
|
|
192
|
+
fontSize: edgeTheme.labelFontSize,
|
|
193
|
+
fontFamily: typography.fontFamily,
|
|
194
|
+
fill: pr.color,
|
|
195
|
+
anchor: 'middle',
|
|
196
|
+
fontWeight: 'bold',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ─── Label de-collision pass ──────────────────────────────────────────────
|
|
201
|
+
// Estimate label bounding boxes, resolve overlaps against fixed rects + siblings
|
|
202
|
+
const CHAR_WIDTH_FACTOR = 0.65; // approximate character width / font size
|
|
203
|
+
const LABEL_PAD = 4;
|
|
204
|
+
// Fixed obstacles: node bounds + caller-provided occupied rects (cell titles, poster title)
|
|
205
|
+
const fixedRects = [...obstacles, ...(occupiedRects ?? [])];
|
|
206
|
+
// Estimate bounding rect for each label (centered at anchor='middle')
|
|
207
|
+
const labelRects = pendingLabels.map(l => {
|
|
208
|
+
const w = l.content.length * l.fontSize * CHAR_WIDTH_FACTOR + LABEL_PAD * 2;
|
|
209
|
+
const h = l.fontSize + LABEL_PAD * 2;
|
|
210
|
+
return { x: l.x - w / 2, y: l.y - h + LABEL_PAD, width: w, height: h };
|
|
211
|
+
});
|
|
212
|
+
// Iteratively push overlapping labels vertically
|
|
213
|
+
deCollideLabels(labelRects, fixedRects);
|
|
214
|
+
// Emit labels at de-collided positions
|
|
215
|
+
for (let i = 0; i < pendingLabels.length; i++) {
|
|
216
|
+
const l = pendingLabels[i];
|
|
217
|
+
const r = labelRects[i];
|
|
218
|
+
// Recover center position from adjusted rect
|
|
219
|
+
const finalX = r.x + r.width / 2;
|
|
220
|
+
const finalY = r.y + r.height - LABEL_PAD;
|
|
221
|
+
elements.push({
|
|
222
|
+
type: 'text',
|
|
223
|
+
content: l.content,
|
|
224
|
+
position: { x: finalX, y: finalY },
|
|
225
|
+
fontSize: l.fontSize,
|
|
226
|
+
fontFamily: l.fontFamily,
|
|
227
|
+
fill: l.fill,
|
|
228
|
+
anchor: l.anchor,
|
|
229
|
+
fontWeight: l.fontWeight,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// Build defs
|
|
233
|
+
if (needsArrow) {
|
|
234
|
+
const s = edgeTheme.arrowSize;
|
|
235
|
+
defs.push(`<marker id="${CROSSLINK_ARROW_ID}" markerWidth="${s}" markerHeight="${s * 0.7}" refX="${s - 1}" refY="${s * 0.35}" orient="auto"><polygon points="0 0, ${s} ${s * 0.35}, 0 ${s * 0.7}" fill="currentColor" /></marker>`);
|
|
236
|
+
}
|
|
237
|
+
if (needsBiArrow) {
|
|
238
|
+
const s = edgeTheme.arrowSize;
|
|
239
|
+
defs.push(`<marker id="${CROSSLINK_ARROW_BOTH_ID}" markerWidth="${s}" markerHeight="${s * 0.7}" refX="1" refY="${s * 0.35}" orient="auto"><polygon points="${s} 0, 0 ${s * 0.35}, ${s} ${s * 0.7}" fill="currentColor" /></marker>`);
|
|
240
|
+
}
|
|
241
|
+
return { defs, elements };
|
|
242
|
+
}
|
|
243
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
244
|
+
function sideToPortDir(side) {
|
|
245
|
+
return side; // CardinalSide and PortDirection use same values
|
|
246
|
+
}
|
|
247
|
+
function edgeStyleToDash(style) {
|
|
248
|
+
switch (style) {
|
|
249
|
+
case 'dashed': return '8 4';
|
|
250
|
+
case 'dotted': return '4 3';
|
|
251
|
+
default: return undefined;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/** Find the midpoint of the longest segment in a polyline. */
|
|
255
|
+
function longestSegmentMidpoint(points) {
|
|
256
|
+
let bestLen = 0;
|
|
257
|
+
let bestMid = points[0];
|
|
258
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
259
|
+
const a = points[i], b = points[i + 1];
|
|
260
|
+
const len = Math.abs(b.x - a.x) + Math.abs(b.y - a.y); // Manhattan length for ortho
|
|
261
|
+
if (len > bestLen) {
|
|
262
|
+
bestLen = len;
|
|
263
|
+
bestMid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return bestMid;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Returns the axis ('h'|'v') and fixed coordinate of the longest segment
|
|
270
|
+
* in a polyline — used to group routes sharing the same corridor.
|
|
271
|
+
*/
|
|
272
|
+
function dominantSegmentInfo(points) {
|
|
273
|
+
let bestLen = 0, axis = 'h', coord = 0;
|
|
274
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
275
|
+
const a = points[i], b = points[i + 1];
|
|
276
|
+
const dx = Math.abs(b.x - a.x), dy = Math.abs(b.y - a.y);
|
|
277
|
+
const len = dx + dy;
|
|
278
|
+
if (len > bestLen) {
|
|
279
|
+
bestLen = len;
|
|
280
|
+
if (dy > dx) {
|
|
281
|
+
axis = 'v';
|
|
282
|
+
coord = (a.x + b.x) / 2;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
axis = 'h';
|
|
286
|
+
coord = (a.y + b.y) / 2;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { axis, coord };
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Returns the point at fraction t (0..1) of total Euclidean path length.
|
|
294
|
+
*/
|
|
295
|
+
function pointAtFraction(points, t) {
|
|
296
|
+
if (points.length === 0)
|
|
297
|
+
return { x: 0, y: 0 };
|
|
298
|
+
if (points.length === 1)
|
|
299
|
+
return points[0];
|
|
300
|
+
const lengths = [];
|
|
301
|
+
let total = 0;
|
|
302
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
303
|
+
const a = points[i], b = points[i + 1];
|
|
304
|
+
const len = Math.hypot(b.x - a.x, b.y - a.y);
|
|
305
|
+
lengths.push(len);
|
|
306
|
+
total += len;
|
|
307
|
+
}
|
|
308
|
+
if (total === 0)
|
|
309
|
+
return points[0];
|
|
310
|
+
const target = total * Math.min(Math.max(t, 0), 1);
|
|
311
|
+
let acc = 0;
|
|
312
|
+
for (let i = 0; i < lengths.length; i++) {
|
|
313
|
+
const l = lengths[i];
|
|
314
|
+
if (acc + l >= target) {
|
|
315
|
+
const frac = l > 0 ? (target - acc) / l : 0;
|
|
316
|
+
const a = points[i], b = points[i + 1];
|
|
317
|
+
return { x: a.x + (b.x - a.x) * frac, y: a.y + (b.y - a.y) * frac };
|
|
318
|
+
}
|
|
319
|
+
acc += l;
|
|
320
|
+
}
|
|
321
|
+
return points[points.length - 1];
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* De-collide label rectangles by pushing them away from fixed obstacles and
|
|
325
|
+
* from each other in the direction of minimum overlap (MTV approach).
|
|
326
|
+
* Horizontal push is used when horizontal overlap is smaller than vertical —
|
|
327
|
+
* this avoids vertical stacking when labels share the same route corridor.
|
|
328
|
+
* Mutates labelRects in place.
|
|
329
|
+
*/
|
|
330
|
+
function deCollideLabels(labelRects, fixedRects) {
|
|
331
|
+
const MAX_PASSES = 20;
|
|
332
|
+
const NUDGE = 2;
|
|
333
|
+
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
334
|
+
let moved = false;
|
|
335
|
+
// Push labels away from fixed rects
|
|
336
|
+
for (const lr of labelRects) {
|
|
337
|
+
for (const fr of fixedRects) {
|
|
338
|
+
if (!rectsOverlap(lr, fr))
|
|
339
|
+
continue;
|
|
340
|
+
const ox = Math.min(lr.x + lr.width, fr.x + fr.width) - Math.max(lr.x, fr.x);
|
|
341
|
+
const oy = Math.min(lr.y + lr.height, fr.y + fr.height) - Math.max(lr.y, fr.y);
|
|
342
|
+
if (ox <= oy) {
|
|
343
|
+
// Horizontal push — less movement
|
|
344
|
+
const lCX = lr.x + lr.width / 2;
|
|
345
|
+
const fCX = fr.x + fr.width / 2;
|
|
346
|
+
lr.x = lCX <= fCX ? fr.x - lr.width - NUDGE : fr.x + fr.width + NUDGE;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
// Vertical push
|
|
350
|
+
const lCY = lr.y + lr.height / 2;
|
|
351
|
+
const fCY = fr.y + fr.height / 2;
|
|
352
|
+
lr.y = lCY <= fCY ? fr.y - lr.height - NUDGE : fr.y + fr.height + NUDGE;
|
|
353
|
+
}
|
|
354
|
+
moved = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Push labels away from each other
|
|
358
|
+
for (let i = 0; i < labelRects.length; i++) {
|
|
359
|
+
for (let j = i + 1; j < labelRects.length; j++) {
|
|
360
|
+
const a = labelRects[i], b = labelRects[j];
|
|
361
|
+
if (!rectsOverlap(a, b))
|
|
362
|
+
continue;
|
|
363
|
+
const ox = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
|
|
364
|
+
const oy = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
|
|
365
|
+
if (ox <= oy) {
|
|
366
|
+
// Push horizontally — shared-corridor case
|
|
367
|
+
const aCX = a.x + a.width / 2;
|
|
368
|
+
const bCX = b.x + b.width / 2;
|
|
369
|
+
if (aCX <= bCX) {
|
|
370
|
+
a.x -= ox / 2;
|
|
371
|
+
b.x += ox / 2;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
a.x += ox / 2;
|
|
375
|
+
b.x -= ox / 2;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Push vertically
|
|
380
|
+
const aCY = a.y + a.height / 2;
|
|
381
|
+
const bCY = b.y + b.height / 2;
|
|
382
|
+
if (aCY <= bCY) {
|
|
383
|
+
const overlap = (a.y + a.height) - b.y;
|
|
384
|
+
a.y -= (overlap + NUDGE) / 2;
|
|
385
|
+
b.y += (overlap + NUDGE) / 2;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
const overlap = (b.y + b.height) - a.y;
|
|
389
|
+
b.y -= (overlap + NUDGE) / 2;
|
|
390
|
+
a.y += (overlap + NUDGE) / 2;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
moved = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (!moved)
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function rectsOverlap(a, b) {
|
|
401
|
+
return a.x < b.x + b.width && a.x + a.width > b.x &&
|
|
402
|
+
a.y < b.y + b.height && a.y + a.height > b.y;
|
|
403
|
+
}
|
|
404
|
+
// ─── Straight Route Crossing Deflection ──────────────────────────────────────
|
|
405
|
+
/**
|
|
406
|
+
* Post-route pass: detect straight routes whose segments cross other routes,
|
|
407
|
+
* and deflect them by inserting a perpendicular waypoint.
|
|
408
|
+
*
|
|
409
|
+
* This handles the case where a straight line doesn't hit any node bounding box
|
|
410
|
+
* but still visually crosses other connector paths.
|
|
411
|
+
*/
|
|
412
|
+
function deflectCrossingStraightRoutes(routes, obstacles) {
|
|
413
|
+
const straightRoutes = routes.filter(r => r.routing === 'straight');
|
|
414
|
+
if (straightRoutes.length === 0)
|
|
415
|
+
return;
|
|
416
|
+
// Collect all segments from OTHER routes for crossing checks.
|
|
417
|
+
// For bezier routes, sample the curve into a polyline (control points aren't line segments).
|
|
418
|
+
const BEZIER_SAMPLES = 16;
|
|
419
|
+
const otherSegments = [];
|
|
420
|
+
for (const r of routes) {
|
|
421
|
+
if (r.routing === 'bezier') {
|
|
422
|
+
const pts = r.points;
|
|
423
|
+
if (pts.length >= 4) {
|
|
424
|
+
const samples = sampleBezierToPolyline(pts[0], pts[1], pts[2], pts[3], BEZIER_SAMPLES);
|
|
425
|
+
for (let i = 0; i < samples.length - 1; i++) {
|
|
426
|
+
otherSegments.push([samples[i], samples[i + 1]]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
const pts = r.points;
|
|
432
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
433
|
+
otherSegments.push([pts[i], pts[i + 1]]);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
for (const sr of straightRoutes) {
|
|
438
|
+
const pts = sr.points;
|
|
439
|
+
if (pts.length !== 2)
|
|
440
|
+
continue;
|
|
441
|
+
const from = pts[0], to = pts[1];
|
|
442
|
+
const dx = to.x - from.x, dy = to.y - from.y;
|
|
443
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
444
|
+
if (len < 30)
|
|
445
|
+
continue;
|
|
446
|
+
// Check if this straight line crosses any other route segment
|
|
447
|
+
const segsExcludingSelf = otherSegments.filter(([a, b]) => !(a === from && b === to));
|
|
448
|
+
const originalCrossings = segsExcludingSelf.filter(([a, b]) => straightSegmentsIntersect(from, to, a, b)).length;
|
|
449
|
+
if (originalCrossings === 0)
|
|
450
|
+
continue;
|
|
451
|
+
// Try perpendicular waypoints at increasing offsets
|
|
452
|
+
const perpX = -dy / len, perpY = dx / len;
|
|
453
|
+
const mid = { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 };
|
|
454
|
+
let bestWp = null;
|
|
455
|
+
let bestCrossings = Infinity;
|
|
456
|
+
for (let offset = len * 0.08; offset <= len * 0.5; offset += len * 0.06) {
|
|
457
|
+
for (const sign of [1, -1]) {
|
|
458
|
+
const wp = { x: mid.x + perpX * offset * sign, y: mid.y + perpY * offset * sign };
|
|
459
|
+
let hitsNode = false;
|
|
460
|
+
for (const obs of obstacles) {
|
|
461
|
+
if (segIntersectsRectInterior(from, wp, obs) || segIntersectsRectInterior(wp, to, obs)) {
|
|
462
|
+
hitsNode = true;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (hitsNode)
|
|
467
|
+
continue;
|
|
468
|
+
let crossings = 0;
|
|
469
|
+
for (const [a, b] of segsExcludingSelf) {
|
|
470
|
+
if (straightSegmentsIntersect(from, wp, a, b))
|
|
471
|
+
crossings++;
|
|
472
|
+
if (straightSegmentsIntersect(wp, to, a, b))
|
|
473
|
+
crossings++;
|
|
474
|
+
}
|
|
475
|
+
if (crossings < bestCrossings) {
|
|
476
|
+
bestCrossings = crossings;
|
|
477
|
+
bestWp = wp;
|
|
478
|
+
if (crossings === 0)
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (bestCrossings === 0)
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
if (bestWp && bestCrossings < originalCrossings) {
|
|
486
|
+
sr.points.splice(1, 0, bestWp);
|
|
487
|
+
sr.routePath = sr.points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
// Deflection failed — fall back to orthogonal routing for this link.
|
|
491
|
+
// This prevents ugly diagonal lines that cross everything.
|
|
492
|
+
const orthoRouter = createRouter('orthogonal');
|
|
493
|
+
const route = orthoRouter.route({
|
|
494
|
+
from, to,
|
|
495
|
+
style: 'orthogonal',
|
|
496
|
+
obstacles,
|
|
497
|
+
padding: 12,
|
|
498
|
+
...(sr.fromDir ? { fromDir: sr.fromDir } : {}),
|
|
499
|
+
...(sr.toDir ? { toDir: sr.toDir } : {}),
|
|
500
|
+
});
|
|
501
|
+
sr.points = [...route.points];
|
|
502
|
+
sr.routePath = undefined; // let Phase 5 rebuild from points
|
|
503
|
+
sr.routing = 'orthogonal'; // so post-route passes treat it correctly
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/** Sample a cubic bezier [from, cp1, cp2, to] into a polyline of n+1 points. */
|
|
508
|
+
function sampleBezierToPolyline(p0, p1, p2, p3, n) {
|
|
509
|
+
const pts = [];
|
|
510
|
+
for (let i = 0; i <= n; i++) {
|
|
511
|
+
const t = i / n;
|
|
512
|
+
const u = 1 - t;
|
|
513
|
+
pts.push({
|
|
514
|
+
x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
|
|
515
|
+
y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
return pts;
|
|
519
|
+
}
|
|
520
|
+
/** Check if two segments properly cross (not just touch). */
|
|
521
|
+
function straightSegmentsIntersect(p1, p2, p3, p4) {
|
|
522
|
+
const d1 = crossProduct(p3, p4, p1);
|
|
523
|
+
const d2 = crossProduct(p3, p4, p2);
|
|
524
|
+
const d3 = crossProduct(p1, p2, p3);
|
|
525
|
+
const d4 = crossProduct(p1, p2, p4);
|
|
526
|
+
return ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
|
|
527
|
+
((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0));
|
|
528
|
+
}
|
|
529
|
+
function crossProduct(a, b, c) {
|
|
530
|
+
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
531
|
+
}
|
|
532
|
+
/** Check if a line segment crosses the interior of a rectangle (Liang-Barsky). */
|
|
533
|
+
function segIntersectsRectInterior(p1, p2, r) {
|
|
534
|
+
const dx = p2.x - p1.x, dy = p2.y - p1.y;
|
|
535
|
+
let tmin = 0, tmax = 1;
|
|
536
|
+
const edges = [
|
|
537
|
+
{ p: -dx, q: p1.x - r.x },
|
|
538
|
+
{ p: dx, q: r.x + r.width - p1.x },
|
|
539
|
+
{ p: -dy, q: p1.y - r.y },
|
|
540
|
+
{ p: dy, q: r.y + r.height - p1.y },
|
|
541
|
+
];
|
|
542
|
+
for (const { p, q } of edges) {
|
|
543
|
+
if (Math.abs(p) < 1e-10) {
|
|
544
|
+
if (q <= 0)
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
const t = q / p;
|
|
549
|
+
if (p < 0)
|
|
550
|
+
tmin = Math.max(tmin, t);
|
|
551
|
+
else
|
|
552
|
+
tmax = Math.min(tmax, t);
|
|
553
|
+
if (tmin >= tmax)
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return tmin < tmax;
|
|
558
|
+
}
|
|
559
|
+
// ─── Bezier Separation ───────────────────────────────────────────────────────
|
|
560
|
+
/**
|
|
561
|
+
* Fan apart bezier curves that share endpoints or cross each other.
|
|
562
|
+
*
|
|
563
|
+
* The BezierRouter stores points as [from, cp1, cp2, to].
|
|
564
|
+
*
|
|
565
|
+
* Phase A: Shared-endpoint fan-out — when multiple curves converge on
|
|
566
|
+
* the same endpoint, offset each curve's nearby control point perpendicular
|
|
567
|
+
* to the from→to axis so they fan apart.
|
|
568
|
+
*
|
|
569
|
+
* Phase B: Crossing resolution — for every pair of bezier curves, sample both
|
|
570
|
+
* and detect segment crossings. If they cross, offset both curves' control
|
|
571
|
+
* points to opposite sides of their respective from→to axes.
|
|
572
|
+
*/
|
|
573
|
+
function separateBezierCurves(routes, gap, obstacles, allRoutes) {
|
|
574
|
+
if (routes.length < 2)
|
|
575
|
+
return;
|
|
576
|
+
const PROXIMITY = 20;
|
|
577
|
+
const groups = [];
|
|
578
|
+
const fromGroups = clusterByEndpoint(routes, 'from', PROXIMITY);
|
|
579
|
+
for (const cluster of fromGroups) {
|
|
580
|
+
if (cluster.length > 1)
|
|
581
|
+
groups.push({ routes: cluster, end: 'from' });
|
|
582
|
+
}
|
|
583
|
+
const toGroups = clusterByEndpoint(routes, 'to', PROXIMITY);
|
|
584
|
+
for (const cluster of toGroups) {
|
|
585
|
+
if (cluster.length > 1)
|
|
586
|
+
groups.push({ routes: cluster, end: 'to' });
|
|
587
|
+
}
|
|
588
|
+
for (const group of groups) {
|
|
589
|
+
const n = group.routes.length;
|
|
590
|
+
for (let i = 0; i < n; i++) {
|
|
591
|
+
const pr = group.routes[i];
|
|
592
|
+
const pts = pr.points;
|
|
593
|
+
if (pts.length < 4)
|
|
594
|
+
continue;
|
|
595
|
+
const from = pts[0], to = pts[pts.length - 1];
|
|
596
|
+
const dx = to.x - from.x, dy = to.y - from.y;
|
|
597
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
598
|
+
if (len < 1)
|
|
599
|
+
continue;
|
|
600
|
+
const perpX = -dy / len, perpY = dx / len;
|
|
601
|
+
const offset = (i - (n - 1) / 2) * gap;
|
|
602
|
+
if (group.end === 'from') {
|
|
603
|
+
pts[1] = { x: pts[1].x + perpX * offset, y: pts[1].y + perpY * offset };
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
pts[pts.length - 2] = {
|
|
607
|
+
x: pts[pts.length - 2].x + perpX * offset,
|
|
608
|
+
y: pts[pts.length - 2].y + perpY * offset,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
rebuildBezierPath(pr);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// ── Phase B: crossing resolution ─────────────────────────────────────────
|
|
615
|
+
// When two beziers cross, try offsetting one curve perpendicular to
|
|
616
|
+
// separate them. If no offset resolves the crossing (e.g. endpoints form
|
|
617
|
+
// an X pattern), fall back one curve to orthogonal routing.
|
|
618
|
+
const SAMPLES = 40;
|
|
619
|
+
for (let i = 0; i < routes.length; i++) {
|
|
620
|
+
for (let j = i + 1; j < routes.length; j++) {
|
|
621
|
+
const ri = routes[i], rj = routes[j];
|
|
622
|
+
const ptsI = ri.points;
|
|
623
|
+
const ptsJ = rj.points;
|
|
624
|
+
if (ptsI.length < 4 || ptsJ.length < 4)
|
|
625
|
+
continue;
|
|
626
|
+
const sampI = sampleBezierCurve(ptsI[0], ptsI[1], ptsI[2], ptsI[3], SAMPLES);
|
|
627
|
+
const sampJ = sampleBezierCurve(ptsJ[0], ptsJ[1], ptsJ[2], ptsJ[3], SAMPLES);
|
|
628
|
+
if (!polylinesIntersect(sampI, sampJ))
|
|
629
|
+
continue;
|
|
630
|
+
// Compute perpendicular for each curve
|
|
631
|
+
const perpOf = (pts) => {
|
|
632
|
+
const dx = pts[3].x - pts[0].x, dy = pts[3].y - pts[0].y;
|
|
633
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
634
|
+
return len < 1 ? null : { px: -dy / len, py: dx / len, len };
|
|
635
|
+
};
|
|
636
|
+
const pI = perpOf(ptsI), pJ = perpOf(ptsJ);
|
|
637
|
+
if (!pI || !pJ)
|
|
638
|
+
continue;
|
|
639
|
+
const origI1 = { ...ptsI[1] }, origI2 = { ...ptsI[2] };
|
|
640
|
+
const origJ1 = { ...ptsJ[1] }, origJ2 = { ...ptsJ[2] };
|
|
641
|
+
let best = null;
|
|
642
|
+
const stepSize = Math.max(gap * 3, 15);
|
|
643
|
+
for (const moveRoute of ['I', 'J']) {
|
|
644
|
+
const p = moveRoute === 'I' ? pI : pJ;
|
|
645
|
+
const pts = moveRoute === 'I' ? ptsI : ptsJ;
|
|
646
|
+
const orig1 = moveRoute === 'I' ? origI1 : origJ1;
|
|
647
|
+
const orig2 = moveRoute === 'I' ? origI2 : origJ2;
|
|
648
|
+
const otherPts = moveRoute === 'I' ? ptsJ : ptsI;
|
|
649
|
+
for (const sign of [1, -1]) {
|
|
650
|
+
for (let step = 1; step <= 8; step++) {
|
|
651
|
+
const off = sign * step * stepSize;
|
|
652
|
+
pts[1] = { x: orig1.x + p.px * off, y: orig1.y + p.py * off };
|
|
653
|
+
pts[2] = { x: orig2.x + p.px * off, y: orig2.y + p.py * off };
|
|
654
|
+
const sA = sampleBezierCurve(pts[0], pts[1], pts[2], pts[3], SAMPLES);
|
|
655
|
+
const sB = sampleBezierCurve(otherPts[0], otherPts[1], otherPts[2], otherPts[3], SAMPLES);
|
|
656
|
+
if (!polylinesIntersect(sA, sB)) {
|
|
657
|
+
if (!best || Math.abs(off) < Math.abs(best.off)) {
|
|
658
|
+
best = { moveRoute, off, px: p.px, py: p.py };
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
// Reset
|
|
664
|
+
pts[1] = { ...orig1 };
|
|
665
|
+
pts[2] = { ...orig2 };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (best) {
|
|
669
|
+
// Apply smallest offset that resolves
|
|
670
|
+
const pts = best.moveRoute === 'I' ? ptsI : ptsJ;
|
|
671
|
+
const orig1 = best.moveRoute === 'I' ? origI1 : origJ1;
|
|
672
|
+
const orig2 = best.moveRoute === 'I' ? origI2 : origJ2;
|
|
673
|
+
pts[1] = { x: orig1.x + best.px * best.off, y: orig1.y + best.py * best.off };
|
|
674
|
+
pts[2] = { x: orig2.x + best.px * best.off, y: orig2.y + best.py * best.off };
|
|
675
|
+
rebuildBezierPath(ri);
|
|
676
|
+
rebuildBezierPath(rj);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
// No offset resolves — endpoints form an X pattern (interleaved on
|
|
680
|
+
// parallel lines). Swap the ports on the shared node so both routes
|
|
681
|
+
// stay on the same side, then re-route as orthogonal.
|
|
682
|
+
swapInterleavedPortsAndReroute(ri, rj, obstacles);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Detect interleaved endpoints and swap ports on the shared line to
|
|
689
|
+
* un-interleave them. Then re-route both as orthogonal.
|
|
690
|
+
*
|
|
691
|
+
* Before: A.from(399,388) A.to(471,597) B.from(367,597) B.to(439,388)
|
|
692
|
+
* y=388: A=399(left) B=439(right) y=597: B=367(left) A=471(right) → interleaved
|
|
693
|
+
* After swap on y=388: A.from↔B.to → A(439,388) B(399,388)
|
|
694
|
+
* y=388: B=399(left) A=439(right) y=597: B=367(left) A=471(right) → consistent
|
|
695
|
+
*/
|
|
696
|
+
function swapInterleavedPortsAndReroute(ri, rj, obstacles) {
|
|
697
|
+
const fromI = ri.points[0], toI = ri.points[ri.points.length - 1];
|
|
698
|
+
const fromJ = rj.points[0], toJ = rj.points[rj.points.length - 1];
|
|
699
|
+
const TOLERANCE = 15;
|
|
700
|
+
// Check all pairings of endpoints that share a horizontal or vertical line
|
|
701
|
+
// and swap to un-interleave.
|
|
702
|
+
const pairs = [];
|
|
703
|
+
// fromI near toJ? (same horizontal/vertical line)
|
|
704
|
+
if (Math.abs(fromI.y - toJ.y) < TOLERANCE)
|
|
705
|
+
pairs.push({ endI: 'from', ptI: fromI, endJ: 'to', ptJ: toJ, axis: 'x' });
|
|
706
|
+
if (Math.abs(fromI.x - toJ.x) < TOLERANCE)
|
|
707
|
+
pairs.push({ endI: 'from', ptI: fromI, endJ: 'to', ptJ: toJ, axis: 'y' });
|
|
708
|
+
// toI near fromJ?
|
|
709
|
+
if (Math.abs(toI.y - fromJ.y) < TOLERANCE)
|
|
710
|
+
pairs.push({ endI: 'to', ptI: toI, endJ: 'from', ptJ: fromJ, axis: 'x' });
|
|
711
|
+
if (Math.abs(toI.x - fromJ.x) < TOLERANCE)
|
|
712
|
+
pairs.push({ endI: 'to', ptI: toI, endJ: 'from', ptJ: fromJ, axis: 'y' });
|
|
713
|
+
// For each pair on a shared line, check if swapping un-interleaves
|
|
714
|
+
for (const pair of pairs) {
|
|
715
|
+
// Get the OTHER endpoints (the ones NOT on this shared line)
|
|
716
|
+
const otherI = pair.endI === 'from' ? toI : fromI;
|
|
717
|
+
const otherJ = pair.endJ === 'from' ? toJ : fromJ;
|
|
718
|
+
// Check interleaving on the shared axis
|
|
719
|
+
const coord = pair.axis; // 'x' means compare x-values on a shared y-line
|
|
720
|
+
const iOnShared = pair.ptI[coord];
|
|
721
|
+
const jOnShared = pair.ptJ[coord];
|
|
722
|
+
const iOther = otherI[coord];
|
|
723
|
+
const jOther = otherJ[coord];
|
|
724
|
+
// Interleaved = one is left/above on shared line but right/below on the other
|
|
725
|
+
const iLeftOnShared = iOnShared < jOnShared;
|
|
726
|
+
const iLeftOnOther = iOther < jOther;
|
|
727
|
+
if (iLeftOnShared === iLeftOnOther)
|
|
728
|
+
continue; // Not interleaved, skip
|
|
729
|
+
// SWAP: exchange the positions on the shared line
|
|
730
|
+
const newPtI = { ...pair.ptJ }; // I takes J's position
|
|
731
|
+
const newPtJ = { ...pair.ptI }; // J takes I's position
|
|
732
|
+
// Keep the shared-line coordinate averaged (same y for horizontal line)
|
|
733
|
+
if (pair.axis === 'x') {
|
|
734
|
+
const avgY = (pair.ptI.y + pair.ptJ.y) / 2;
|
|
735
|
+
newPtI.y = avgY;
|
|
736
|
+
newPtJ.y = avgY;
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
const avgX = (pair.ptI.x + pair.ptJ.x) / 2;
|
|
740
|
+
newPtI.x = avgX;
|
|
741
|
+
newPtJ.x = avgX;
|
|
742
|
+
}
|
|
743
|
+
// Build new from/to for each route
|
|
744
|
+
const newFromI = pair.endI === 'from' ? newPtI : fromI;
|
|
745
|
+
const newToI = pair.endI === 'to' ? newPtI : toI;
|
|
746
|
+
const newFromJ = pair.endJ === 'from' ? newPtJ : fromJ;
|
|
747
|
+
const newToJ = pair.endJ === 'to' ? newPtJ : toJ;
|
|
748
|
+
// Re-route both as orthogonal and check crossing
|
|
749
|
+
const orthoRouter = createRouter('orthogonal');
|
|
750
|
+
const routeI = orthoRouter.route({ from: newFromI, to: newToI, style: 'orthogonal', obstacles, padding: 12 });
|
|
751
|
+
const routeJ = orthoRouter.route({ from: newFromJ, to: newToJ, style: 'orthogonal', obstacles, padding: 12 });
|
|
752
|
+
const polyI = routeI.points;
|
|
753
|
+
const polyJ = routeJ.points;
|
|
754
|
+
let crosses = false;
|
|
755
|
+
for (let a = 0; a < polyI.length - 1 && !crosses; a++)
|
|
756
|
+
for (let b = 0; b < polyJ.length - 1 && !crosses; b++)
|
|
757
|
+
if (straightSegmentsIntersect(polyI[a], polyI[a + 1], polyJ[b], polyJ[b + 1]))
|
|
758
|
+
crosses = true;
|
|
759
|
+
if (!crosses) {
|
|
760
|
+
// Apply
|
|
761
|
+
ri.points = [...routeI.points];
|
|
762
|
+
ri.routePath = undefined;
|
|
763
|
+
ri.routing = 'orthogonal';
|
|
764
|
+
rj.points = [...routeJ.points];
|
|
765
|
+
rj.routePath = undefined;
|
|
766
|
+
rj.routing = 'orthogonal';
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
// Fallback: just convert both to orthogonal with natural ports
|
|
771
|
+
convertToOrthogonal(ri, obstacles);
|
|
772
|
+
convertToOrthogonal(rj, obstacles);
|
|
773
|
+
}
|
|
774
|
+
/** Convert a PendingRoute to orthogonal routing. */
|
|
775
|
+
function convertToOrthogonal(target, obstacles) {
|
|
776
|
+
const from = target.points[0], to = target.points[target.points.length - 1];
|
|
777
|
+
const orthoRouter = createRouter('orthogonal');
|
|
778
|
+
const route = orthoRouter.route({
|
|
779
|
+
from, to,
|
|
780
|
+
style: 'orthogonal',
|
|
781
|
+
obstacles,
|
|
782
|
+
padding: 12,
|
|
783
|
+
});
|
|
784
|
+
target.points = [...route.points];
|
|
785
|
+
target.routePath = undefined;
|
|
786
|
+
target.routing = 'orthogonal';
|
|
787
|
+
}
|
|
788
|
+
/** Rebuild the routePath string from [from, cp1, cp2, to] points. */
|
|
789
|
+
function rebuildBezierPath(pr) {
|
|
790
|
+
const pts = pr.points;
|
|
791
|
+
pr.routePath = `M ${pts[0].x} ${pts[0].y} C ${pts[1].x} ${pts[1].y} ${pts[2].x} ${pts[2].y} ${pts[3].x} ${pts[3].y}`;
|
|
792
|
+
}
|
|
793
|
+
/** Offset both control points of a bezier curve perpendicular to its from→to axis. */
|
|
794
|
+
function offsetBezierPerp(pts, offset) {
|
|
795
|
+
const from = pts[0], to = pts[3];
|
|
796
|
+
const dx = to.x - from.x, dy = to.y - from.y;
|
|
797
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
798
|
+
if (len < 1)
|
|
799
|
+
return;
|
|
800
|
+
const perpX = -dy / len, perpY = dx / len;
|
|
801
|
+
pts[1] = { x: pts[1].x + perpX * offset, y: pts[1].y + perpY * offset };
|
|
802
|
+
pts[2] = { x: pts[2].x + perpX * offset, y: pts[2].y + perpY * offset };
|
|
803
|
+
}
|
|
804
|
+
/** Sample a cubic bezier into a polyline of N+1 points. */
|
|
805
|
+
function sampleBezierCurve(p0, p1, p2, p3, n) {
|
|
806
|
+
const pts = [];
|
|
807
|
+
for (let i = 0; i <= n; i++) {
|
|
808
|
+
const t = i / n;
|
|
809
|
+
const u = 1 - t;
|
|
810
|
+
pts.push({
|
|
811
|
+
x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
|
|
812
|
+
y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
return pts;
|
|
816
|
+
}
|
|
817
|
+
/** Check if two polylines have any crossing segments. */
|
|
818
|
+
function polylinesIntersect(a, b) {
|
|
819
|
+
for (let i = 0; i < a.length - 1; i++) {
|
|
820
|
+
for (let j = 0; j < b.length - 1; j++) {
|
|
821
|
+
if (segmentsIntersect(a[i], a[i + 1], b[j], b[j + 1]))
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
/** Check if two line segments p1-p2 and p3-p4 intersect (proper crossing only). */
|
|
828
|
+
function segmentsIntersect(p1, p2, p3, p4) {
|
|
829
|
+
const d1 = cross(p3, p4, p1);
|
|
830
|
+
const d2 = cross(p3, p4, p2);
|
|
831
|
+
const d3 = cross(p1, p2, p3);
|
|
832
|
+
const d4 = cross(p1, p2, p4);
|
|
833
|
+
if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
|
|
834
|
+
((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
function cross(a, b, c) {
|
|
840
|
+
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
841
|
+
}
|
|
842
|
+
/** Cluster bezier routes by proximity of their from or to endpoint. */
|
|
843
|
+
function clusterByEndpoint(routes, end, proximity) {
|
|
844
|
+
const clusters = [];
|
|
845
|
+
const assigned = new Set();
|
|
846
|
+
for (let i = 0; i < routes.length; i++) {
|
|
847
|
+
const ri = routes[i];
|
|
848
|
+
if (assigned.has(ri))
|
|
849
|
+
continue;
|
|
850
|
+
const pi = end === 'from' ? ri.points[0] : ri.points[ri.points.length - 1];
|
|
851
|
+
const cluster = [ri];
|
|
852
|
+
assigned.add(ri);
|
|
853
|
+
for (let j = i + 1; j < routes.length; j++) {
|
|
854
|
+
const rj = routes[j];
|
|
855
|
+
if (assigned.has(rj))
|
|
856
|
+
continue;
|
|
857
|
+
const pj = end === 'from' ? rj.points[0] : rj.points[rj.points.length - 1];
|
|
858
|
+
const dx = pi.x - pj.x, dy = pi.y - pj.y;
|
|
859
|
+
if (Math.sqrt(dx * dx + dy * dy) < proximity) {
|
|
860
|
+
cluster.push(rj);
|
|
861
|
+
assigned.add(rj);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
clusters.push(cluster);
|
|
865
|
+
}
|
|
866
|
+
return clusters;
|
|
867
|
+
}
|
|
868
|
+
// ─── Border Nudging ──────────────────────────────────────────────────────────
|
|
869
|
+
/**
|
|
870
|
+
* Nudge segments that run parallel along cell borders (walls) away from them.
|
|
871
|
+
* A horizontal segment at y ≈ border.y is shifted vertically.
|
|
872
|
+
* A vertical segment at x ≈ border.x is shifted horizontally.
|
|
873
|
+
* Only affects "running along" — perpendicular crossings are fine.
|
|
874
|
+
*/
|
|
875
|
+
function nudgeOffBorders(routes, borders) {
|
|
876
|
+
const TOLERANCE = 3; // within 3px of a border edge
|
|
877
|
+
const NUDGE = 8; // push 8px away
|
|
878
|
+
// Extract unique horizontal border Y values and vertical border X values
|
|
879
|
+
const hBorderYs = []; // horizontal borders (wide, thin height)
|
|
880
|
+
const vBorderXs = []; // vertical borders (tall, thin width)
|
|
881
|
+
for (const b of borders) {
|
|
882
|
+
if (b.width > b.height) {
|
|
883
|
+
// Horizontal border — center Y
|
|
884
|
+
hBorderYs.push(b.y + b.height / 2);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
// Vertical border — center X
|
|
888
|
+
vBorderXs.push(b.x + b.width / 2);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
for (const route of routes) {
|
|
892
|
+
const pts = route.points;
|
|
893
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
894
|
+
const a = pts[i], b = pts[i + 1];
|
|
895
|
+
const dx = Math.abs(b.x - a.x), dy = Math.abs(b.y - a.y);
|
|
896
|
+
if (dy < 1 && dx > 1) {
|
|
897
|
+
// Horizontal segment — check against horizontal borders
|
|
898
|
+
for (const borderY of hBorderYs) {
|
|
899
|
+
if (Math.abs(a.y - borderY) < TOLERANCE) {
|
|
900
|
+
// Nudge toward the midpoint between from/to Y of the full route
|
|
901
|
+
const routeMinY = Math.min(...pts.map(p => p.y));
|
|
902
|
+
const routeMaxY = Math.max(...pts.map(p => p.y));
|
|
903
|
+
const routeMidY = (routeMinY + routeMaxY) / 2;
|
|
904
|
+
const nudgeDir = a.y < routeMidY ? -NUDGE : NUDGE;
|
|
905
|
+
pts[i] = { x: a.x, y: a.y + nudgeDir };
|
|
906
|
+
pts[i + 1] = { x: b.x, y: b.y + nudgeDir };
|
|
907
|
+
// Also adjust adjacent vertical segment endpoints
|
|
908
|
+
if (i > 0 && Math.abs(pts[i - 1].x - a.x) < 1) {
|
|
909
|
+
pts[i - 1] = { ...pts[i - 1], y: pts[i - 1].y }; // keep as-is, the vertex moved
|
|
910
|
+
}
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
else if (dx < 1 && dy > 1) {
|
|
916
|
+
// Vertical segment — check against vertical borders
|
|
917
|
+
for (const borderX of vBorderXs) {
|
|
918
|
+
if (Math.abs(a.x - borderX) < TOLERANCE) {
|
|
919
|
+
const routeMinX = Math.min(...pts.map(p => p.x));
|
|
920
|
+
const routeMaxX = Math.max(...pts.map(p => p.x));
|
|
921
|
+
const routeMidX = (routeMinX + routeMaxX) / 2;
|
|
922
|
+
const nudgeDir = a.x < routeMidX ? -NUDGE : NUDGE;
|
|
923
|
+
pts[i] = { x: a.x + nudgeDir, y: a.y };
|
|
924
|
+
pts[i + 1] = { x: b.x + nudgeDir, y: b.y };
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Detect overlapping parallel segments across all routes and offset them
|
|
934
|
+
* so they don't visually merge. Mutates route points in place.
|
|
935
|
+
*/
|
|
936
|
+
function separateOverlappingChannels(routes, gap) {
|
|
937
|
+
const segments = [];
|
|
938
|
+
for (let r = 0; r < routes.length; r++) {
|
|
939
|
+
const pts = routes[r].points;
|
|
940
|
+
for (let s = 0; s < pts.length - 1; s++) {
|
|
941
|
+
const a = pts[s], b = pts[s + 1];
|
|
942
|
+
const dx = Math.abs(b.x - a.x), dy = Math.abs(b.y - a.y);
|
|
943
|
+
if (dx < 1 && dy > 1) {
|
|
944
|
+
// Vertical segment
|
|
945
|
+
segments.push({ routeIdx: r, segIdx: s, isVertical: true, coord: a.x, min: Math.min(a.y, b.y), max: Math.max(a.y, b.y) });
|
|
946
|
+
}
|
|
947
|
+
else if (dy < 1 && dx > 1) {
|
|
948
|
+
// Horizontal segment
|
|
949
|
+
segments.push({ routeIdx: r, segIdx: s, isVertical: false, coord: a.y, min: Math.min(a.x, b.x), max: Math.max(a.x, b.x) });
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
// Group segments that share the same axis, similar coord, and overlapping range
|
|
954
|
+
const COORD_TOLERANCE = 10;
|
|
955
|
+
const processed = new Set();
|
|
956
|
+
for (let i = 0; i < segments.length; i++) {
|
|
957
|
+
if (processed.has(i))
|
|
958
|
+
continue;
|
|
959
|
+
const group = [i];
|
|
960
|
+
const si = segments[i];
|
|
961
|
+
for (let j = i + 1; j < segments.length; j++) {
|
|
962
|
+
if (processed.has(j))
|
|
963
|
+
continue;
|
|
964
|
+
const sj = segments[j];
|
|
965
|
+
if (si.isVertical !== sj.isVertical)
|
|
966
|
+
continue;
|
|
967
|
+
if (Math.abs(si.coord - sj.coord) > COORD_TOLERANCE)
|
|
968
|
+
continue;
|
|
969
|
+
// Check range overlap
|
|
970
|
+
if (si.max <= sj.min || sj.max <= si.min)
|
|
971
|
+
continue;
|
|
972
|
+
group.push(j);
|
|
973
|
+
}
|
|
974
|
+
if (group.length < 2)
|
|
975
|
+
continue;
|
|
976
|
+
// Offset each segment in the group
|
|
977
|
+
const n = group.length;
|
|
978
|
+
for (let k = 0; k < n; k++) {
|
|
979
|
+
const offset = (k - (n - 1) / 2) * gap;
|
|
980
|
+
const seg = segments[group[k]];
|
|
981
|
+
processed.add(group[k]);
|
|
982
|
+
// Mutate the points in the route
|
|
983
|
+
const pts = routes[seg.routeIdx].points;
|
|
984
|
+
if (seg.isVertical) {
|
|
985
|
+
// Offset x coordinate of both endpoints of this segment
|
|
986
|
+
pts[seg.segIdx] = { x: pts[seg.segIdx].x + offset, y: pts[seg.segIdx].y };
|
|
987
|
+
pts[seg.segIdx + 1] = { x: pts[seg.segIdx + 1].x + offset, y: pts[seg.segIdx + 1].y };
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
// Offset y coordinate
|
|
991
|
+
pts[seg.segIdx] = { x: pts[seg.segIdx].x, y: pts[seg.segIdx].y + offset };
|
|
992
|
+
pts[seg.segIdx + 1] = { x: pts[seg.segIdx + 1].x, y: pts[seg.segIdx + 1].y + offset };
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
//# sourceMappingURL=render.js.map
|