@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,849 @@
|
|
|
1
|
+
// ─── Straight ─────────────────────────────────────────────────────────────────
|
|
2
|
+
class StraightRouter {
|
|
3
|
+
route({ from, to, obstacles, padding }) {
|
|
4
|
+
const pad = padding ?? 12;
|
|
5
|
+
// If no obstacles or no crossing, use a direct line
|
|
6
|
+
if (!obstacles || obstacles.length === 0 ||
|
|
7
|
+
!straightHitsObstacles(from, to, obstacles, pad)) {
|
|
8
|
+
return {
|
|
9
|
+
points: [from, to],
|
|
10
|
+
path: `M ${from.x} ${from.y} L ${to.x} ${to.y}`,
|
|
11
|
+
labelPosition: midpoint(from, to),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// Deflect: find a waypoint that avoids all obstacles.
|
|
15
|
+
// Try perpendicular offsets of increasing magnitude in both directions.
|
|
16
|
+
const dx = to.x - from.x, dy = to.y - from.y;
|
|
17
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
18
|
+
if (len < 1) {
|
|
19
|
+
return { points: [from, to], path: `M ${from.x} ${from.y} L ${to.x} ${to.y}`, labelPosition: from };
|
|
20
|
+
}
|
|
21
|
+
const perpX = -dy / len, perpY = dx / len;
|
|
22
|
+
const mid = midpoint(from, to);
|
|
23
|
+
for (let offset = len * 0.1; offset <= len * 0.6; offset += len * 0.1) {
|
|
24
|
+
for (const sign of [1, -1]) {
|
|
25
|
+
const wp = { x: mid.x + perpX * offset * sign, y: mid.y + perpY * offset * sign };
|
|
26
|
+
if (!straightHitsObstacles(from, wp, obstacles, pad) &&
|
|
27
|
+
!straightHitsObstacles(wp, to, obstacles, pad)) {
|
|
28
|
+
return {
|
|
29
|
+
points: [from, wp, to],
|
|
30
|
+
path: `M ${from.x} ${from.y} L ${wp.x} ${wp.y} L ${to.x} ${to.y}`,
|
|
31
|
+
labelPosition: wp,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Fallback: direct line (best effort)
|
|
37
|
+
return {
|
|
38
|
+
points: [from, to],
|
|
39
|
+
path: `M ${from.x} ${from.y} L ${to.x} ${to.y}`,
|
|
40
|
+
labelPosition: midpoint(from, to),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Check if a straight line from→to passes through any obstacle interior. */
|
|
45
|
+
function straightHitsObstacles(from, to, obstacles, pad) {
|
|
46
|
+
for (const obs of obstacles) {
|
|
47
|
+
if (segCrossesInterior(from, to, {
|
|
48
|
+
x: obs.x - pad, y: obs.y - pad,
|
|
49
|
+
width: obs.width + 2 * pad, height: obs.height + 2 * pad,
|
|
50
|
+
}, 0)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// ─── Orthogonal ───────────────────────────────────────────────────────────────
|
|
57
|
+
class OrthogonalRouter {
|
|
58
|
+
route({ from, to, fromDir, toDir, obstacles, padding }) {
|
|
59
|
+
const dx = to.x - from.x;
|
|
60
|
+
const dy = to.y - from.y;
|
|
61
|
+
const midX = (from.x + to.x) / 2;
|
|
62
|
+
const midY = (from.y + to.y) / 2;
|
|
63
|
+
const pad = padding ?? 12;
|
|
64
|
+
// Minimum straight "landing" run before an arrowhead / after an exit port,
|
|
65
|
+
// so a same-wall detour never places the arrowhead directly on a bend.
|
|
66
|
+
const STUB = Math.max(pad, 20);
|
|
67
|
+
// Already aligned on one axis — straight line. But only shortcut when the
|
|
68
|
+
// segment actually leaves the source along its wall normal, approaches the
|
|
69
|
+
// target from that wall's outboard side, and crosses no obstacles.
|
|
70
|
+
const straightH = Math.abs(dy) < 1; // horizontal straight line
|
|
71
|
+
const straightV = Math.abs(dx) < 1; // vertical straight line
|
|
72
|
+
if (straightH || straightV) {
|
|
73
|
+
const dirsOk = straightDirsOk(from, to, straightH, straightV, fromDir, toDir);
|
|
74
|
+
const clear = !obstacles || countRouteCollisions([from, to], obstacles) === 0;
|
|
75
|
+
if (dirsOk && clear) {
|
|
76
|
+
return {
|
|
77
|
+
points: [from, to],
|
|
78
|
+
path: `M ${from.x} ${from.y} L ${to.x} ${to.y}`,
|
|
79
|
+
labelPosition: { x: midX, y: midY },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// With port direction hints, route so the first segment leaves in
|
|
84
|
+
// fromDir and the last segment arrives along toDir.
|
|
85
|
+
if (fromDir && toDir) {
|
|
86
|
+
const exitH = fromDir === 'E' || fromDir === 'W';
|
|
87
|
+
const entryH = toDir === 'E' || toDir === 'W';
|
|
88
|
+
let points;
|
|
89
|
+
let path;
|
|
90
|
+
if (exitH && entryH) {
|
|
91
|
+
// Both horizontal — bend at midX, shifted to avoid obstacles.
|
|
92
|
+
// For a same-wall pair (both E or both W) the two stubs point the same
|
|
93
|
+
// way, so the connecting segment must sit OUTSIDE both ports, otherwise
|
|
94
|
+
// the route collapses back onto the wall.
|
|
95
|
+
const sameWall = fromDir === toDir;
|
|
96
|
+
const baseX = sameWall
|
|
97
|
+
? fromDir === 'E'
|
|
98
|
+
? Math.max(from.x, to.x) + STUB
|
|
99
|
+
: Math.min(from.x, to.x) - STUB
|
|
100
|
+
: midX;
|
|
101
|
+
let bendX;
|
|
102
|
+
if (sameWall) {
|
|
103
|
+
// The connecting channel must stay OUTBOARD of the shared wall. The
|
|
104
|
+
// generic avoidance keeps the bend within the port x-range, which for
|
|
105
|
+
// a same-wall pair pulls it to the INBOARD side — driving the landing
|
|
106
|
+
// segment straight through the target box to reach the far wall
|
|
107
|
+
// ("behind the target"). Only ever shift the channel further outboard.
|
|
108
|
+
bendX = clearBendXOutboard(baseX, from, to, obstacles, pad, fromDir === 'E' ? 1 : -1);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
bendX = clearVerticalBend(baseX, from.y, to.y, from.x, to.x, obstacles, pad);
|
|
112
|
+
bendX = clearHorizontalSegments(bendX, from, to, obstacles, pad);
|
|
113
|
+
}
|
|
114
|
+
const v1 = { x: bendX, y: from.y };
|
|
115
|
+
const v2 = { x: bendX, y: to.y };
|
|
116
|
+
let hhPoints = [from, v1, v2, to];
|
|
117
|
+
if (sameWall && obstacles && routeHitsEndpointObstacle(hhPoints, from, to, obstacles)) {
|
|
118
|
+
hhPoints = buildSameHorizontalWallRoute(from, to, fromDir, obstacles, pad, STUB);
|
|
119
|
+
}
|
|
120
|
+
// Verify the H+H route is clear; if not, try V+V with direction stubs.
|
|
121
|
+
// Skip the fallback for same-wall pairs: the outboard H+H route is the
|
|
122
|
+
// one that honours the wall hint without crossing the target, so we keep
|
|
123
|
+
// it even if it grazes other cells.
|
|
124
|
+
if (!sameWall && obstacles && countRouteCollisions(hhPoints, obstacles) > 0) {
|
|
125
|
+
const vvRoute = buildVVRouteWithStubs(from, to, fromDir, toDir, obstacles, pad);
|
|
126
|
+
if (countRouteCollisions(vvRoute, obstacles) < countRouteCollisions(hhPoints, obstacles)) {
|
|
127
|
+
points = vvRoute;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
points = hhPoints;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
points = hhPoints;
|
|
135
|
+
}
|
|
136
|
+
path = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
|
|
137
|
+
}
|
|
138
|
+
else if (!exitH && !entryH) {
|
|
139
|
+
// Both vertical — bend at midY, shifted to avoid obstacles.
|
|
140
|
+
// For a same-wall pair (both N or both S) push the connecting segment
|
|
141
|
+
// OUTSIDE both ports so the route arcs over/under the nodes.
|
|
142
|
+
const sameWall = fromDir === toDir;
|
|
143
|
+
const baseY = sameWall
|
|
144
|
+
? fromDir === 'S'
|
|
145
|
+
? Math.max(from.y, to.y) + STUB
|
|
146
|
+
: Math.min(from.y, to.y) - STUB
|
|
147
|
+
: midY;
|
|
148
|
+
let bendY;
|
|
149
|
+
if (sameWall) {
|
|
150
|
+
// Same reasoning as the horizontal case: keep the connecting channel
|
|
151
|
+
// outboard of the shared wall so the landing segment never passes
|
|
152
|
+
// through the target box.
|
|
153
|
+
bendY = clearBendYOutboard(baseY, from, to, obstacles, pad, fromDir === 'S' ? 1 : -1);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
bendY = clearHorizontalBend(baseY, from.x, to.x, from.y, to.y, obstacles, pad);
|
|
157
|
+
bendY = clearVerticalSegments(bendY, from, to, obstacles, pad);
|
|
158
|
+
}
|
|
159
|
+
const v1 = { x: from.x, y: bendY };
|
|
160
|
+
const v2 = { x: to.x, y: bendY };
|
|
161
|
+
let vvPoints = [from, v1, v2, to];
|
|
162
|
+
if (sameWall && obstacles && routeHitsEndpointObstacle(vvPoints, from, to, obstacles)) {
|
|
163
|
+
vvPoints = buildSameVerticalWallRoute(from, to, fromDir, obstacles, pad, STUB);
|
|
164
|
+
}
|
|
165
|
+
// Verify the V+V route is clear; if not, try H+H with direction stubs.
|
|
166
|
+
// Same-wall pairs keep their outboard V+V route (see horizontal case).
|
|
167
|
+
if (!sameWall && obstacles && countRouteCollisions(vvPoints, obstacles) > 0) {
|
|
168
|
+
const hhRoute = buildHHRouteWithStubs(from, to, fromDir, toDir, obstacles, pad);
|
|
169
|
+
if (countRouteCollisions(hhRoute, obstacles) < countRouteCollisions(vvPoints, obstacles)) {
|
|
170
|
+
points = hhRoute;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
points = vvPoints;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
points = vvPoints;
|
|
178
|
+
}
|
|
179
|
+
path = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
|
|
180
|
+
}
|
|
181
|
+
else if (exitH && !entryH) {
|
|
182
|
+
// Exit horizontal, enter vertical — single bend
|
|
183
|
+
const corner = { x: to.x, y: from.y };
|
|
184
|
+
points = [from, corner, to];
|
|
185
|
+
path = `M ${from.x} ${from.y} L ${corner.x} ${corner.y} L ${to.x} ${to.y}`;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Exit vertical, enter horizontal — single bend
|
|
189
|
+
const corner = { x: from.x, y: to.y };
|
|
190
|
+
points = [from, corner, to];
|
|
191
|
+
path = `M ${from.x} ${from.y} L ${corner.x} ${corner.y} L ${to.x} ${to.y}`;
|
|
192
|
+
}
|
|
193
|
+
return { points, path, labelPosition: { x: midX, y: midY } };
|
|
194
|
+
}
|
|
195
|
+
// Fallback: no direction hints — heuristic based on geometry
|
|
196
|
+
let points;
|
|
197
|
+
let path;
|
|
198
|
+
if (Math.abs(dy) >= Math.abs(dx)) {
|
|
199
|
+
// Mainly vertical — bend at mid-Y
|
|
200
|
+
const bendY = clearHorizontalBend(midY, from.x, to.x, from.y, to.y, obstacles, pad);
|
|
201
|
+
const v1 = { x: from.x, y: bendY };
|
|
202
|
+
const v2 = { x: to.x, y: bendY };
|
|
203
|
+
points = [from, v1, v2, to];
|
|
204
|
+
path = `M ${from.x} ${from.y} L ${v1.x} ${v1.y} L ${v2.x} ${v2.y} L ${to.x} ${to.y}`;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// Mainly horizontal — bend at mid-X
|
|
208
|
+
const bendX = clearVerticalBend(midX, from.y, to.y, from.x, to.x, obstacles, pad);
|
|
209
|
+
const v1 = { x: bendX, y: from.y };
|
|
210
|
+
const v2 = { x: bendX, y: to.y };
|
|
211
|
+
points = [from, v1, v2, to];
|
|
212
|
+
path = `M ${from.x} ${from.y} L ${v1.x} ${v1.y} L ${v2.x} ${v2.y} L ${to.x} ${to.y}`;
|
|
213
|
+
}
|
|
214
|
+
return { points, path, labelPosition: { x: midX, y: midY } };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Clear a same-wall vertical bend channel by shifting it ONLY further outboard
|
|
219
|
+
* (in `dir`: -1 = west, +1 = east). Unlike clearVerticalBend, this never pulls
|
|
220
|
+
* the channel back toward the ports — for a same-wall pair the inboard side is
|
|
221
|
+
* across the target box, so an inboard shift would route the landing segment
|
|
222
|
+
* "behind the target". The channel and both horizontal stubs are checked; on a
|
|
223
|
+
* hit the channel jumps past the obstacle's outboard edge.
|
|
224
|
+
*/
|
|
225
|
+
function clearBendXOutboard(baseX, from, to, obstacles, pad, dir) {
|
|
226
|
+
if (!obstacles || obstacles.length === 0)
|
|
227
|
+
return baseX;
|
|
228
|
+
let x = baseX;
|
|
229
|
+
const yMin = Math.min(from.y, to.y);
|
|
230
|
+
const yMax = Math.max(from.y, to.y);
|
|
231
|
+
for (let pass = 0; pass < obstacles.length * 2 + 2; pass++) {
|
|
232
|
+
let moved = false;
|
|
233
|
+
for (const obs of obstacles) {
|
|
234
|
+
const oLeft = obs.x - pad;
|
|
235
|
+
const oRight = obs.x + obs.width + pad;
|
|
236
|
+
const oTop = obs.y;
|
|
237
|
+
const oBottom = obs.y + obs.height;
|
|
238
|
+
const vHits = x > oLeft && x < oRight && yMax > oTop && yMin < oBottom;
|
|
239
|
+
const hHitTo = segCrossesInterior({ x, y: to.y }, { x: to.x, y: to.y }, obs, pad);
|
|
240
|
+
const hHitFrom = segCrossesInterior({ x: from.x, y: from.y }, { x, y: from.y }, obs, pad);
|
|
241
|
+
if (vHits || hHitTo || hHitFrom) {
|
|
242
|
+
const edge = dir < 0 ? oLeft : oRight;
|
|
243
|
+
if ((dir < 0 && edge < x) || (dir > 0 && edge > x)) {
|
|
244
|
+
x = edge;
|
|
245
|
+
moved = true;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!moved)
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
return x;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Vertical-wall analogue of clearBendXOutboard: shift a same-wall horizontal
|
|
257
|
+
* bend line only further outboard (dir: -1 = north, +1 = south).
|
|
258
|
+
*/
|
|
259
|
+
function clearBendYOutboard(baseY, from, to, obstacles, pad, dir) {
|
|
260
|
+
if (!obstacles || obstacles.length === 0)
|
|
261
|
+
return baseY;
|
|
262
|
+
let y = baseY;
|
|
263
|
+
const xMin = Math.min(from.x, to.x);
|
|
264
|
+
const xMax = Math.max(from.x, to.x);
|
|
265
|
+
for (let pass = 0; pass < obstacles.length * 2 + 2; pass++) {
|
|
266
|
+
let moved = false;
|
|
267
|
+
for (const obs of obstacles) {
|
|
268
|
+
const oTop = obs.y - pad;
|
|
269
|
+
const oBottom = obs.y + obs.height + pad;
|
|
270
|
+
const oLeft = obs.x;
|
|
271
|
+
const oRight = obs.x + obs.width;
|
|
272
|
+
const hHits = y > oTop && y < oBottom && xMax > oLeft && xMin < oRight;
|
|
273
|
+
const vHitTo = segCrossesInterior({ x: to.x, y }, { x: to.x, y: to.y }, obs, pad);
|
|
274
|
+
const vHitFrom = segCrossesInterior({ x: from.x, y: from.y }, { x: from.x, y }, obs, pad);
|
|
275
|
+
if (hHits || vHitTo || vHitFrom) {
|
|
276
|
+
const edge = dir < 0 ? oTop : oBottom;
|
|
277
|
+
if ((dir < 0 && edge < y) || (dir > 0 && edge > y)) {
|
|
278
|
+
y = edge;
|
|
279
|
+
moved = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!moved)
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
return y;
|
|
288
|
+
}
|
|
289
|
+
function straightDirsOk(from, to, straightH, straightV, fromDir, toDir) {
|
|
290
|
+
if (!fromDir && !toDir)
|
|
291
|
+
return true;
|
|
292
|
+
const dx = to.x - from.x;
|
|
293
|
+
const dy = to.y - from.y;
|
|
294
|
+
if (straightH && Math.abs(dx) < 1)
|
|
295
|
+
return true;
|
|
296
|
+
let requiredFrom;
|
|
297
|
+
let requiredTo;
|
|
298
|
+
if (straightH) {
|
|
299
|
+
requiredFrom = dx > 0 ? 'E' : 'W';
|
|
300
|
+
requiredTo = dx > 0 ? 'W' : 'E';
|
|
301
|
+
}
|
|
302
|
+
else if (straightV) {
|
|
303
|
+
requiredFrom = dy > 0 ? 'S' : 'N';
|
|
304
|
+
requiredTo = dy > 0 ? 'N' : 'S';
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
return (!fromDir || fromDir === requiredFrom) && (!toDir || toDir === requiredTo);
|
|
310
|
+
}
|
|
311
|
+
function buildSameVerticalWallRoute(from, to, wall, obstacles, pad, stub) {
|
|
312
|
+
const dir = wall === 'S' ? 1 : -1;
|
|
313
|
+
const fromOut = { x: from.x, y: from.y + dir * stub };
|
|
314
|
+
const toOut = { x: to.x, y: to.y + dir * stub };
|
|
315
|
+
const colliders = collidingObstacles([from, fromOut, { x: to.x, y: fromOut.y }, to], obstacles);
|
|
316
|
+
const relevant = colliders.length > 0 ? colliders : obstacles;
|
|
317
|
+
const leftX = Math.min(...relevant.map(o => o.x - pad));
|
|
318
|
+
const rightX = Math.max(...relevant.map(o => o.x + o.width + pad));
|
|
319
|
+
return bestSameVerticalCandidate(from, to, fromOut, toOut, [leftX, rightX], obstacles);
|
|
320
|
+
}
|
|
321
|
+
function buildSameHorizontalWallRoute(from, to, wall, obstacles, pad, stub) {
|
|
322
|
+
const dir = wall === 'E' ? 1 : -1;
|
|
323
|
+
const fromOut = { x: from.x + dir * stub, y: from.y };
|
|
324
|
+
const toOut = { x: to.x + dir * stub, y: to.y };
|
|
325
|
+
const colliders = collidingObstacles([from, fromOut, { x: fromOut.x, y: to.y }, to], obstacles);
|
|
326
|
+
const relevant = colliders.length > 0 ? colliders : obstacles;
|
|
327
|
+
const topY = Math.min(...relevant.map(o => o.y - pad));
|
|
328
|
+
const bottomY = Math.max(...relevant.map(o => o.y + o.height + pad));
|
|
329
|
+
return bestSameHorizontalCandidate(from, to, fromOut, toOut, [topY, bottomY], obstacles);
|
|
330
|
+
}
|
|
331
|
+
function bestSameVerticalCandidate(from, to, fromOut, toOut, escapeXs, obstacles) {
|
|
332
|
+
let best;
|
|
333
|
+
let bestCost = Infinity;
|
|
334
|
+
for (const escapeX of escapeXs) {
|
|
335
|
+
const candidate = [
|
|
336
|
+
from,
|
|
337
|
+
fromOut,
|
|
338
|
+
{ x: escapeX, y: fromOut.y },
|
|
339
|
+
{ x: escapeX, y: toOut.y },
|
|
340
|
+
toOut,
|
|
341
|
+
to,
|
|
342
|
+
];
|
|
343
|
+
const cost = countRouteCollisions(candidate, obstacles) * 1_000_000 + polylineLength(candidate);
|
|
344
|
+
if (cost < bestCost) {
|
|
345
|
+
best = candidate;
|
|
346
|
+
bestCost = cost;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return best;
|
|
350
|
+
}
|
|
351
|
+
function bestSameHorizontalCandidate(from, to, fromOut, toOut, escapeYs, obstacles) {
|
|
352
|
+
let best;
|
|
353
|
+
let bestCost = Infinity;
|
|
354
|
+
for (const escapeY of escapeYs) {
|
|
355
|
+
const candidate = [
|
|
356
|
+
from,
|
|
357
|
+
fromOut,
|
|
358
|
+
{ x: fromOut.x, y: escapeY },
|
|
359
|
+
{ x: toOut.x, y: escapeY },
|
|
360
|
+
toOut,
|
|
361
|
+
to,
|
|
362
|
+
];
|
|
363
|
+
const cost = countRouteCollisions(candidate, obstacles) * 1_000_000 + polylineLength(candidate);
|
|
364
|
+
if (cost < bestCost) {
|
|
365
|
+
best = candidate;
|
|
366
|
+
bestCost = cost;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return best;
|
|
370
|
+
}
|
|
371
|
+
function collidingObstacles(points, obstacles) {
|
|
372
|
+
return obstacles.filter(obs => {
|
|
373
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
374
|
+
if (segCrossesInterior(points[i], points[i + 1], obs, 0))
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
return false;
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
function routeHitsEndpointObstacle(points, from, to, obstacles) {
|
|
381
|
+
return collidingObstacles(points, obstacles).some(obs => pointInOrOnRect(from, obs) || pointInOrOnRect(to, obs));
|
|
382
|
+
}
|
|
383
|
+
function pointInOrOnRect(p, r) {
|
|
384
|
+
const eps = 1e-6;
|
|
385
|
+
return p.x >= r.x - eps && p.x <= r.x + r.width + eps &&
|
|
386
|
+
p.y >= r.y - eps && p.y <= r.y + r.height + eps;
|
|
387
|
+
}
|
|
388
|
+
function polylineLength(points) {
|
|
389
|
+
let len = 0;
|
|
390
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
391
|
+
const a = points[i];
|
|
392
|
+
const b = points[i + 1];
|
|
393
|
+
len += Math.abs(b.x - a.x) + Math.abs(b.y - a.y);
|
|
394
|
+
}
|
|
395
|
+
return len;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Shift a vertical bend line (at x=bendX, spanning yMin–yMax) to avoid obstacles.
|
|
399
|
+
* Returns the original bendX if it doesn't intersect anything, otherwise shifts
|
|
400
|
+
* to the nearest clear side of all intersecting obstacles.
|
|
401
|
+
*/
|
|
402
|
+
function clearVerticalBend(bendX, y1, y2, fromX, toX, obstacles, pad) {
|
|
403
|
+
if (!obstacles || obstacles.length === 0)
|
|
404
|
+
return bendX;
|
|
405
|
+
const yMin = Math.min(y1, y2);
|
|
406
|
+
const yMax = Math.max(y1, y2);
|
|
407
|
+
// Iteratively shift until clear of all obstacles (max passes to avoid infinite loop)
|
|
408
|
+
let shifted = bendX;
|
|
409
|
+
for (let pass = 0; pass < obstacles.length; pass++) {
|
|
410
|
+
let hit = false;
|
|
411
|
+
for (const obs of obstacles) {
|
|
412
|
+
const oLeft = obs.x - pad;
|
|
413
|
+
const oRight = obs.x + obs.width + pad;
|
|
414
|
+
const oTop = obs.y;
|
|
415
|
+
const oBottom = obs.y + obs.height;
|
|
416
|
+
if (shifted > oLeft && shifted < oRight && yMax > oTop && yMin < oBottom) {
|
|
417
|
+
const leftCandidate = oLeft;
|
|
418
|
+
const rightCandidate = oRight;
|
|
419
|
+
const minX = Math.min(fromX, toX);
|
|
420
|
+
const maxX = Math.max(fromX, toX);
|
|
421
|
+
const leftValid = leftCandidate >= minX;
|
|
422
|
+
const rightValid = rightCandidate <= maxX;
|
|
423
|
+
if (leftValid && rightValid) {
|
|
424
|
+
shifted = (shifted - leftCandidate < rightCandidate - shifted)
|
|
425
|
+
? leftCandidate : rightCandidate;
|
|
426
|
+
}
|
|
427
|
+
else if (leftValid) {
|
|
428
|
+
shifted = leftCandidate;
|
|
429
|
+
}
|
|
430
|
+
else if (rightValid) {
|
|
431
|
+
shifted = rightCandidate;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
shifted = (shifted - leftCandidate < rightCandidate - shifted)
|
|
435
|
+
? leftCandidate : rightCandidate;
|
|
436
|
+
}
|
|
437
|
+
hit = true;
|
|
438
|
+
break; // re-check from start with new position
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (!hit)
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
return shifted;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Shift a horizontal bend line (at y=bendY, spanning xMin–xMax) to avoid obstacles.
|
|
448
|
+
*/
|
|
449
|
+
function clearHorizontalBend(bendY, x1, x2, fromY, toY, obstacles, pad) {
|
|
450
|
+
if (!obstacles || obstacles.length === 0)
|
|
451
|
+
return bendY;
|
|
452
|
+
const xMin = Math.min(x1, x2);
|
|
453
|
+
const xMax = Math.max(x1, x2);
|
|
454
|
+
// Iteratively shift until clear of all obstacles
|
|
455
|
+
let shifted = bendY;
|
|
456
|
+
for (let pass = 0; pass < obstacles.length; pass++) {
|
|
457
|
+
let hit = false;
|
|
458
|
+
for (const obs of obstacles) {
|
|
459
|
+
const oTop = obs.y - pad;
|
|
460
|
+
const oBottom = obs.y + obs.height + pad;
|
|
461
|
+
const oLeft = obs.x;
|
|
462
|
+
const oRight = obs.x + obs.width;
|
|
463
|
+
if (shifted > oTop && shifted < oBottom && xMax > oLeft && xMin < oRight) {
|
|
464
|
+
const topCandidate = oTop;
|
|
465
|
+
const bottomCandidate = oBottom;
|
|
466
|
+
const minY = Math.min(fromY, toY);
|
|
467
|
+
const maxY = Math.max(fromY, toY);
|
|
468
|
+
const topValid = topCandidate >= minY;
|
|
469
|
+
const bottomValid = bottomCandidate <= maxY;
|
|
470
|
+
if (topValid && bottomValid) {
|
|
471
|
+
shifted = (shifted - topCandidate < bottomCandidate - shifted)
|
|
472
|
+
? topCandidate : bottomCandidate;
|
|
473
|
+
}
|
|
474
|
+
else if (topValid) {
|
|
475
|
+
shifted = topCandidate;
|
|
476
|
+
}
|
|
477
|
+
else if (bottomValid) {
|
|
478
|
+
shifted = bottomCandidate;
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
shifted = (shifted - topCandidate < bottomCandidate - shifted)
|
|
482
|
+
? topCandidate : bottomCandidate;
|
|
483
|
+
}
|
|
484
|
+
hit = true;
|
|
485
|
+
break; // re-check from start with new position
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (!hit)
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
return shifted;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* After placing a vertical bend at bendX for an H→V→H route (from→(bendX, from.y)→(bendX, to.y)→to),
|
|
495
|
+
* verify that the two horizontal segments don't cross obstacles. If they do, shift bendX
|
|
496
|
+
* so it's on the target/source side of the obstacle.
|
|
497
|
+
* If conflicting (fixing one breaks the other), push bendX outside all obstacles.
|
|
498
|
+
*/
|
|
499
|
+
function clearHorizontalSegments(bendX, from, to, obstacles, pad) {
|
|
500
|
+
if (!obstacles || obstacles.length === 0)
|
|
501
|
+
return bendX;
|
|
502
|
+
let shifted = bendX;
|
|
503
|
+
let prevShifted = NaN;
|
|
504
|
+
for (let pass = 0; pass < obstacles.length * 2; pass++) {
|
|
505
|
+
// Detect oscillation (conflicting constraints)
|
|
506
|
+
if (shifted === prevShifted)
|
|
507
|
+
break;
|
|
508
|
+
prevShifted = shifted;
|
|
509
|
+
let hit = false;
|
|
510
|
+
for (const obs of obstacles) {
|
|
511
|
+
// Check last horizontal: shifted → to.x at y=to.y
|
|
512
|
+
if (segCrossesInterior({ x: shifted, y: to.y }, { x: to.x, y: to.y }, obs, pad)) {
|
|
513
|
+
shifted = (to.x < obs.x) ? obs.x - pad : obs.x + obs.width + pad;
|
|
514
|
+
hit = true;
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
// Check first horizontal: from.x → shifted at y=from.y
|
|
518
|
+
if (segCrossesInterior({ x: from.x, y: from.y }, { x: shifted, y: from.y }, obs, pad)) {
|
|
519
|
+
shifted = (from.x < obs.x) ? obs.x - pad : obs.x + obs.width + pad;
|
|
520
|
+
hit = true;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (!hit)
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
// Final verification — if still colliding, push outside all obstacles
|
|
528
|
+
const stillCollides = obstacles.some(obs => segCrossesInterior({ x: shifted, y: to.y }, { x: to.x, y: to.y }, obs, pad) ||
|
|
529
|
+
segCrossesInterior({ x: from.x, y: from.y }, { x: shifted, y: from.y }, obs, pad));
|
|
530
|
+
if (stillCollides) {
|
|
531
|
+
// Compute the leftmost and rightmost obstacle edges
|
|
532
|
+
let globalLeft = Infinity, globalRight = -Infinity;
|
|
533
|
+
for (const obs of obstacles) {
|
|
534
|
+
globalLeft = Math.min(globalLeft, obs.x - pad);
|
|
535
|
+
globalRight = Math.max(globalRight, obs.x + obs.width + pad);
|
|
536
|
+
}
|
|
537
|
+
// Push to whichever side is closer to the target
|
|
538
|
+
const leftDist = Math.abs(to.x - globalLeft) + Math.abs(from.x - globalLeft);
|
|
539
|
+
const rightDist = Math.abs(to.x - globalRight) + Math.abs(from.x - globalRight);
|
|
540
|
+
shifted = leftDist < rightDist ? globalLeft : globalRight;
|
|
541
|
+
}
|
|
542
|
+
return shifted;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* After placing a horizontal bend at bendY for a V→H→V route (from→(from.x, bendY)→(to.x, bendY)→to),
|
|
546
|
+
* verify that the two vertical segments don't cross obstacles. If they do, shift bendY.
|
|
547
|
+
* If conflicting, push outside all obstacles.
|
|
548
|
+
*/
|
|
549
|
+
function clearVerticalSegments(bendY, from, to, obstacles, pad) {
|
|
550
|
+
if (!obstacles || obstacles.length === 0)
|
|
551
|
+
return bendY;
|
|
552
|
+
let shifted = bendY;
|
|
553
|
+
let prevShifted = NaN;
|
|
554
|
+
for (let pass = 0; pass < obstacles.length * 2; pass++) {
|
|
555
|
+
if (shifted === prevShifted)
|
|
556
|
+
break;
|
|
557
|
+
prevShifted = shifted;
|
|
558
|
+
let hit = false;
|
|
559
|
+
for (const obs of obstacles) {
|
|
560
|
+
if (segCrossesInterior({ x: to.x, y: shifted }, { x: to.x, y: to.y }, obs, pad)) {
|
|
561
|
+
shifted = (to.y < obs.y) ? obs.y - pad : obs.y + obs.height + pad;
|
|
562
|
+
hit = true;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
if (segCrossesInterior({ x: from.x, y: from.y }, { x: from.x, y: shifted }, obs, pad)) {
|
|
566
|
+
shifted = (from.y < obs.y) ? obs.y - pad : obs.y + obs.height + pad;
|
|
567
|
+
hit = true;
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (!hit)
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
// Final verification
|
|
575
|
+
const stillCollides = obstacles.some(obs => segCrossesInterior({ x: to.x, y: shifted }, { x: to.x, y: to.y }, obs, pad) ||
|
|
576
|
+
segCrossesInterior({ x: from.x, y: from.y }, { x: from.x, y: shifted }, obs, pad));
|
|
577
|
+
if (stillCollides) {
|
|
578
|
+
let globalTop = Infinity, globalBottom = -Infinity;
|
|
579
|
+
for (const obs of obstacles) {
|
|
580
|
+
globalTop = Math.min(globalTop, obs.y - pad);
|
|
581
|
+
globalBottom = Math.max(globalBottom, obs.y + obs.height + pad);
|
|
582
|
+
}
|
|
583
|
+
const topDist = Math.abs(to.y - globalTop) + Math.abs(from.y - globalTop);
|
|
584
|
+
const bottomDist = Math.abs(to.y - globalBottom) + Math.abs(from.y - globalBottom);
|
|
585
|
+
shifted = topDist < bottomDist ? globalTop : globalBottom;
|
|
586
|
+
}
|
|
587
|
+
return shifted;
|
|
588
|
+
}
|
|
589
|
+
/** Test if segment p1→p2 crosses the strict interior of obs (boundary touches don't count). */
|
|
590
|
+
function segCrossesInterior(p1, p2, obs, _pad) {
|
|
591
|
+
const dx = p2.x - p1.x, dy = p2.y - p1.y;
|
|
592
|
+
const xmin = obs.x, xmax = obs.x + obs.width;
|
|
593
|
+
const ymin = obs.y, ymax = obs.y + obs.height;
|
|
594
|
+
let tmin = 0, tmax = 1;
|
|
595
|
+
const edges = [
|
|
596
|
+
{ p: -dx, q: p1.x - xmin },
|
|
597
|
+
{ p: dx, q: xmax - p1.x },
|
|
598
|
+
{ p: -dy, q: p1.y - ymin },
|
|
599
|
+
{ p: dy, q: ymax - p1.y },
|
|
600
|
+
];
|
|
601
|
+
for (const { p, q } of edges) {
|
|
602
|
+
if (Math.abs(p) < 1e-10) {
|
|
603
|
+
// Parallel: segment must be strictly inside (not on boundary) to count
|
|
604
|
+
if (q <= 0)
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
const t = q / p;
|
|
609
|
+
if (p < 0)
|
|
610
|
+
tmin = Math.max(tmin, t);
|
|
611
|
+
else
|
|
612
|
+
tmax = Math.min(tmax, t);
|
|
613
|
+
if (tmin > tmax)
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return tmin < tmax; // strict interior crossing only
|
|
618
|
+
}
|
|
619
|
+
/** Count total segment-obstacle collisions along a polyline route. */
|
|
620
|
+
export function countRouteCollisions(points, obstacles) {
|
|
621
|
+
let count = 0;
|
|
622
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
623
|
+
for (const obs of obstacles) {
|
|
624
|
+
if (segCrossesInterior(points[i], points[i + 1], obs, 0))
|
|
625
|
+
count++;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return count;
|
|
629
|
+
}
|
|
630
|
+
/** Build a V→H→V route (vertical-first) with obstacle avoidance. */
|
|
631
|
+
function buildVVRoute(from, to, obstacles, pad) {
|
|
632
|
+
const midY = (from.y + to.y) / 2;
|
|
633
|
+
let bendY = clearHorizontalBend(midY, from.x, to.x, from.y, to.y, obstacles, pad);
|
|
634
|
+
bendY = clearVerticalSegments(bendY, from, to, obstacles, pad);
|
|
635
|
+
return [from, { x: from.x, y: bendY }, { x: to.x, y: bendY }, to];
|
|
636
|
+
}
|
|
637
|
+
/** Build an H→V→H route (horizontal-first) with obstacle avoidance. */
|
|
638
|
+
function buildHHRoute(from, to, obstacles, pad) {
|
|
639
|
+
const midX = (from.x + to.x) / 2;
|
|
640
|
+
let bendX = clearVerticalBend(midX, from.y, to.y, from.x, to.x, obstacles, pad);
|
|
641
|
+
bendX = clearHorizontalSegments(bendX, from, to, obstacles, pad);
|
|
642
|
+
return [from, { x: bendX, y: from.y }, { x: bendX, y: to.y }, to];
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Build a V+V fallback route that honours H exit/entry port directions.
|
|
646
|
+
* Adds short horizontal stubs at start/end, then routes V→H→V in between.
|
|
647
|
+
* Route shape: from → stub1 → (stub1.x, bendY) → (stub2.x, bendY) → stub2 → to
|
|
648
|
+
*/
|
|
649
|
+
function buildVVRouteWithStubs(from, to, fromDir, toDir, obstacles, pad) {
|
|
650
|
+
// Stub must be long enough to look intentional (at least 24px)
|
|
651
|
+
const stubLen = Math.max(pad, 24);
|
|
652
|
+
const stub1 = {
|
|
653
|
+
x: from.x + (fromDir === 'W' ? -stubLen : fromDir === 'E' ? stubLen : 0),
|
|
654
|
+
y: from.y + (fromDir === 'N' ? -stubLen : fromDir === 'S' ? stubLen : 0),
|
|
655
|
+
};
|
|
656
|
+
const stub2 = {
|
|
657
|
+
x: to.x + (toDir === 'W' ? -stubLen : toDir === 'E' ? stubLen : 0),
|
|
658
|
+
y: to.y + (toDir === 'N' ? -stubLen : toDir === 'S' ? stubLen : 0),
|
|
659
|
+
};
|
|
660
|
+
// Route V+V between the stubs
|
|
661
|
+
const midY = (stub1.y + stub2.y) / 2;
|
|
662
|
+
let bendY = clearHorizontalBend(midY, stub1.x, stub2.x, stub1.y, stub2.y, obstacles, pad);
|
|
663
|
+
bendY = clearVerticalSegments(bendY, stub1, stub2, obstacles, pad);
|
|
664
|
+
return [from, stub1, { x: stub1.x, y: bendY }, { x: stub2.x, y: bendY }, stub2, to];
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Build an H+H fallback route that honours V exit/entry port directions.
|
|
668
|
+
* Adds short vertical stubs at start/end, then routes H→V→H in between.
|
|
669
|
+
*/
|
|
670
|
+
function buildHHRouteWithStubs(from, to, fromDir, toDir, obstacles, pad) {
|
|
671
|
+
const stubLen = Math.max(pad, 24);
|
|
672
|
+
const stub1 = {
|
|
673
|
+
x: from.x + (fromDir === 'W' ? -stubLen : fromDir === 'E' ? stubLen : 0),
|
|
674
|
+
y: from.y + (fromDir === 'N' ? -stubLen : fromDir === 'S' ? stubLen : 0),
|
|
675
|
+
};
|
|
676
|
+
const stub2 = {
|
|
677
|
+
x: to.x + (toDir === 'W' ? -stubLen : toDir === 'E' ? stubLen : 0),
|
|
678
|
+
y: to.y + (toDir === 'N' ? -stubLen : toDir === 'S' ? stubLen : 0),
|
|
679
|
+
};
|
|
680
|
+
const midX = (stub1.x + stub2.x) / 2;
|
|
681
|
+
let bendX = clearVerticalBend(midX, stub1.y, stub2.y, stub1.x, stub2.x, obstacles, pad);
|
|
682
|
+
bendX = clearHorizontalSegments(bendX, stub1, stub2, obstacles, pad);
|
|
683
|
+
return [from, stub1, { x: bendX, y: stub1.y }, { x: bendX, y: stub2.y }, stub2, to];
|
|
684
|
+
}
|
|
685
|
+
// ─── Bezier ───────────────────────────────────────────────────────────────────
|
|
686
|
+
class BezierRouter {
|
|
687
|
+
route({ from, to, tension, obstacles, padding }) {
|
|
688
|
+
const t = tension ?? 0.4;
|
|
689
|
+
const dx = to.x - from.x;
|
|
690
|
+
const dy = to.y - from.y;
|
|
691
|
+
const pad = padding ?? 12;
|
|
692
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
693
|
+
// Cap the control point pull to avoid exaggerated arcs on long routes.
|
|
694
|
+
// Pull scales with distance but is clamped to a sensible maximum.
|
|
695
|
+
const MAX_PULL = 150;
|
|
696
|
+
let cp1;
|
|
697
|
+
let cp2;
|
|
698
|
+
if (Math.abs(dy) >= Math.abs(dx)) {
|
|
699
|
+
const pull = Math.min(Math.abs(dy) * t, MAX_PULL);
|
|
700
|
+
cp1 = { x: from.x, y: from.y + (dy >= 0 ? pull : -pull) };
|
|
701
|
+
cp2 = { x: to.x, y: to.y + (dy >= 0 ? -pull : pull) };
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
const pull = Math.min(Math.abs(dx) * t, MAX_PULL);
|
|
705
|
+
cp1 = { x: from.x + (dx >= 0 ? pull : -pull), y: from.y };
|
|
706
|
+
cp2 = { x: to.x + (dx >= 0 ? -pull : pull), y: to.y };
|
|
707
|
+
}
|
|
708
|
+
// Check for obstacle collisions and adjust control points if needed
|
|
709
|
+
if (obstacles && obstacles.length > 0) {
|
|
710
|
+
const adjusted = avoidObstaclesBezier(from, to, cp1, cp2, obstacles, pad, t);
|
|
711
|
+
cp1 = adjusted.cp1;
|
|
712
|
+
cp2 = adjusted.cp2;
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
points: [from, cp1, cp2, to],
|
|
716
|
+
path: `M ${from.x} ${from.y} C ${cp1.x} ${cp1.y} ${cp2.x} ${cp2.y} ${to.x} ${to.y}`,
|
|
717
|
+
labelPosition: midpoint(from, to),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Adjust bezier control points to avoid obstacles.
|
|
723
|
+
*
|
|
724
|
+
* Strategy: sample the curve and check for obstacle hits.
|
|
725
|
+
* If the default curve collides, try offsetting control points
|
|
726
|
+
* perpendicular to the from→to axis in both directions.
|
|
727
|
+
* Pick the direction with fewer collisions. If both still collide,
|
|
728
|
+
* increase the perpendicular offset until clear (up to a limit).
|
|
729
|
+
*/
|
|
730
|
+
function avoidObstaclesBezier(from, to, cp1, cp2, obstacles, pad, tension) {
|
|
731
|
+
const SAMPLES = 16;
|
|
732
|
+
// Check if default curve hits any obstacle
|
|
733
|
+
if (!bezierHitsObstacles(from, cp1, cp2, to, obstacles, pad, SAMPLES)) {
|
|
734
|
+
return { cp1, cp2 };
|
|
735
|
+
}
|
|
736
|
+
// Perpendicular to from→to
|
|
737
|
+
const dx = to.x - from.x, dy = to.y - from.y;
|
|
738
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
739
|
+
if (len < 1)
|
|
740
|
+
return { cp1, cp2 };
|
|
741
|
+
const perpX = -dy / len;
|
|
742
|
+
const perpY = dx / len;
|
|
743
|
+
// The from→to midpoint — prefer curves that stay close to this
|
|
744
|
+
const mid = midpoint(from, to);
|
|
745
|
+
// Try increasing perpendicular offsets; at each step, try BOTH directions
|
|
746
|
+
// and prefer the one whose curve stays closer to the from→to midpoint (inner arc).
|
|
747
|
+
// Cap maximum offset to avoid pushing curves far outside the content area.
|
|
748
|
+
const MAX_OFFSET = Math.min(len * 0.5, 80);
|
|
749
|
+
for (let offset = len * 0.1; offset <= MAX_OFFSET; offset += len * 0.1) {
|
|
750
|
+
const cp1p = { x: cp1.x + perpX * offset, y: cp1.y + perpY * offset };
|
|
751
|
+
const cp2p = { x: cp2.x + perpX * offset, y: cp2.y + perpY * offset };
|
|
752
|
+
const clearP = !bezierHitsObstacles(from, cp1p, cp2p, to, obstacles, pad, SAMPLES);
|
|
753
|
+
const cp1n = { x: cp1.x - perpX * offset, y: cp1.y - perpY * offset };
|
|
754
|
+
const cp2n = { x: cp2.x - perpX * offset, y: cp2.y - perpY * offset };
|
|
755
|
+
const clearN = !bezierHitsObstacles(from, cp1n, cp2n, to, obstacles, pad, SAMPLES);
|
|
756
|
+
if (clearP && clearN) {
|
|
757
|
+
// Both clear — pick the one whose curve midpoint is closer to from→to midpoint
|
|
758
|
+
const midP = sampleBezier(from, cp1p, cp2p, to, 0.5);
|
|
759
|
+
const midN = sampleBezier(from, cp1n, cp2n, to, 0.5);
|
|
760
|
+
const distP = (midP.x - mid.x) ** 2 + (midP.y - mid.y) ** 2;
|
|
761
|
+
const distN = (midN.x - mid.x) ** 2 + (midN.y - mid.y) ** 2;
|
|
762
|
+
return distP <= distN ? { cp1: cp1p, cp2: cp2p } : { cp1: cp1n, cp2: cp2n };
|
|
763
|
+
}
|
|
764
|
+
if (clearP)
|
|
765
|
+
return { cp1: cp1p, cp2: cp2p };
|
|
766
|
+
if (clearN)
|
|
767
|
+
return { cp1: cp1n, cp2: cp2n };
|
|
768
|
+
}
|
|
769
|
+
// Fallback: return the least-colliding direction at capped offset, preferring inner
|
|
770
|
+
const maxOff = MAX_OFFSET;
|
|
771
|
+
const cp1p = { x: cp1.x + perpX * maxOff, y: cp1.y + perpY * maxOff };
|
|
772
|
+
const cp2p = { x: cp2.x + perpX * maxOff, y: cp2.y + perpY * maxOff };
|
|
773
|
+
const cp1n = { x: cp1.x - perpX * maxOff, y: cp1.y - perpY * maxOff };
|
|
774
|
+
const cp2n = { x: cp2.x - perpX * maxOff, y: cp2.y - perpY * maxOff };
|
|
775
|
+
const hitsP = countBezierHits(from, cp1p, cp2p, to, obstacles, pad, SAMPLES);
|
|
776
|
+
const hitsN = countBezierHits(from, cp1n, cp2n, to, obstacles, pad, SAMPLES);
|
|
777
|
+
if (hitsP === hitsN) {
|
|
778
|
+
const midP = sampleBezier(from, cp1p, cp2p, to, 0.5);
|
|
779
|
+
const midN = sampleBezier(from, cp1n, cp2n, to, 0.5);
|
|
780
|
+
const distP = (midP.x - mid.x) ** 2 + (midP.y - mid.y) ** 2;
|
|
781
|
+
const distN = (midN.x - mid.x) ** 2 + (midN.y - mid.y) ** 2;
|
|
782
|
+
return distP <= distN ? { cp1: cp1p, cp2: cp2p } : { cp1: cp1n, cp2: cp2n };
|
|
783
|
+
}
|
|
784
|
+
return hitsP < hitsN ? { cp1: cp1p, cp2: cp2p } : { cp1: cp1n, cp2: cp2n };
|
|
785
|
+
}
|
|
786
|
+
/** Sample a cubic bezier and check if any sample is inside an obstacle. */
|
|
787
|
+
function bezierHitsObstacles(p0, p1, p2, p3, obstacles, pad, samples) {
|
|
788
|
+
for (let i = 1; i < samples; i++) {
|
|
789
|
+
const t = i / samples;
|
|
790
|
+
const pt = sampleBezier(p0, p1, p2, p3, t);
|
|
791
|
+
for (const obs of obstacles) {
|
|
792
|
+
if (pt.x > obs.x - pad && pt.x < obs.x + obs.width + pad &&
|
|
793
|
+
pt.y > obs.y - pad && pt.y < obs.y + obs.height + pad)
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
/** Count obstacle hits along a bezier curve. */
|
|
800
|
+
function countBezierHits(p0, p1, p2, p3, obstacles, pad, samples) {
|
|
801
|
+
let count = 0;
|
|
802
|
+
for (let i = 1; i < samples; i++) {
|
|
803
|
+
const t = i / samples;
|
|
804
|
+
const pt = sampleBezier(p0, p1, p2, p3, t);
|
|
805
|
+
for (const obs of obstacles) {
|
|
806
|
+
if (pt.x > obs.x - pad && pt.x < obs.x + obs.width + pad &&
|
|
807
|
+
pt.y > obs.y - pad && pt.y < obs.y + obs.height + pad)
|
|
808
|
+
count++;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return count;
|
|
812
|
+
}
|
|
813
|
+
/** Evaluate a cubic bezier at parameter t ∈ [0, 1]. */
|
|
814
|
+
function sampleBezier(p0, p1, p2, p3, t) {
|
|
815
|
+
const u = 1 - t;
|
|
816
|
+
return {
|
|
817
|
+
x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
|
|
818
|
+
y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
// ─── Polyline ─────────────────────────────────────────────────────────────────
|
|
822
|
+
// Placeholder — routes straight until explicit waypoints are in RouteRequest.
|
|
823
|
+
class PolylineRouter {
|
|
824
|
+
route(req) {
|
|
825
|
+
return new StraightRouter().route(req);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
// ─── Singleton Instances ──────────────────────────────────────────────────────
|
|
829
|
+
export const straightRouter = new StraightRouter();
|
|
830
|
+
export const orthogonalRouter = new OrthogonalRouter();
|
|
831
|
+
export const bezierRouter = new BezierRouter();
|
|
832
|
+
export const polylineRouter = new PolylineRouter();
|
|
833
|
+
// ─── Factory & Default ────────────────────────────────────────────────────────
|
|
834
|
+
/** Create (or look up) a router by style name. */
|
|
835
|
+
export function createRouter(style) {
|
|
836
|
+
switch (style) {
|
|
837
|
+
case 'straight': return straightRouter;
|
|
838
|
+
case 'orthogonal': return orthogonalRouter;
|
|
839
|
+
case 'bezier': return bezierRouter;
|
|
840
|
+
case 'polyline': return polylineRouter;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/** Default router used by layout engines unless overridden. */
|
|
844
|
+
export const defaultRouter = orthogonalRouter;
|
|
845
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
846
|
+
function midpoint(a, b) {
|
|
847
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
848
|
+
}
|
|
849
|
+
//# sourceMappingURL=router.js.map
|