@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,877 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file diagrams/class/layout.ts — UML class diagram.
|
|
3
|
+
*
|
|
4
|
+
* Classes render as three-compartment boxes (name/stereotype, attributes,
|
|
5
|
+
* methods); placement uses the shared layered kernel (inheritance points up).
|
|
6
|
+
* Relationships draw as straight connectors clipped to box borders with
|
|
7
|
+
* UML end markers (triangle / diamond / arrow) plus cardinality + labels.
|
|
8
|
+
*/
|
|
9
|
+
import { pen } from '../../../scene/build.js';
|
|
10
|
+
import { applyOverlays } from '../../../overlay/apply.js';
|
|
11
|
+
import { measureText } from '../../../text/metrics.js';
|
|
12
|
+
import { layeredLayout, routeEdge } from '../../../graph/layered.js';
|
|
13
|
+
import { borderPoint } from '../../../graph/connect.js';
|
|
14
|
+
import { rhu, rhuInt } from '../../../util/round.js';
|
|
15
|
+
/** True if axis-aligned segment (x1,y1)→(x2,y2) passes through box interior. */
|
|
16
|
+
function segmentIntersectsBox(x1, y1, x2, y2, box) {
|
|
17
|
+
const bx1 = box.x, bx2 = box.x + box.width;
|
|
18
|
+
const by1 = box.y, by2 = box.y + box.height;
|
|
19
|
+
if (y1 === y2) {
|
|
20
|
+
const lx = Math.min(x1, x2), rx = Math.max(x1, x2);
|
|
21
|
+
return y1 > by1 && y1 < by2 && lx < bx2 && rx > bx1;
|
|
22
|
+
}
|
|
23
|
+
const ty = Math.min(y1, y2), by = Math.max(y1, y2);
|
|
24
|
+
return x1 > bx1 && x1 < bx2 && ty < by2 && by > by1;
|
|
25
|
+
}
|
|
26
|
+
/** Convert a segment to a 2px-wide axis-aligned rect. */
|
|
27
|
+
function toRect(x1, y1, x2, y2) {
|
|
28
|
+
const W = 1;
|
|
29
|
+
return {
|
|
30
|
+
x1: Math.min(x1, x2) - W, y1: Math.min(y1, y2) - W,
|
|
31
|
+
x2: Math.max(x1, x2) + W, y2: Math.max(y1, y2) + W,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Returns the shared axis length between two RoutedSegment rects. */
|
|
35
|
+
function rectsOverlapLength(a, b) {
|
|
36
|
+
const ox = Math.min(a.x2, b.x2) - Math.max(a.x1, b.x1);
|
|
37
|
+
const oy = Math.min(a.y2, b.y2) - Math.max(a.y1, b.y1);
|
|
38
|
+
if (ox <= 0 || oy <= 0)
|
|
39
|
+
return 0;
|
|
40
|
+
return Math.max(ox, oy);
|
|
41
|
+
}
|
|
42
|
+
const CLEARANCE = 12; // obstacle avoidance (right-side, inter-column)
|
|
43
|
+
const LANE_CLEARANCE = 48; // visual breathing room for left/right margin lanes
|
|
44
|
+
/** True if point (lx, ly) falls inside any of the given boxes. */
|
|
45
|
+
function labelInBox(lx, ly, boxes, padding = 8) {
|
|
46
|
+
return boxes.some(b => lx > b.x - padding && lx < b.x + b.width + padding &&
|
|
47
|
+
ly > b.y - padding && ly < b.y + b.height + padding);
|
|
48
|
+
}
|
|
49
|
+
/** Remove values within tol pixels of each other (keep first occurrence). */
|
|
50
|
+
function dedup(arr, tol) {
|
|
51
|
+
const result = [];
|
|
52
|
+
for (const v of arr) {
|
|
53
|
+
if (!result.some(r => Math.abs(r - v) < tol))
|
|
54
|
+
result.push(v);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
/** Render a segment list to an SVG path string. */
|
|
59
|
+
function segmentsToPath(segs) {
|
|
60
|
+
if (segs.length === 0)
|
|
61
|
+
return '';
|
|
62
|
+
if (segs.length === 1) {
|
|
63
|
+
return `M ${rhu(segs[0][0])} ${rhu(segs[0][1])} L ${rhu(segs[0][2])} ${rhu(segs[0][3])}`;
|
|
64
|
+
}
|
|
65
|
+
const pts = [`M ${rhu(segs[0][0])} ${rhu(segs[0][1])}`];
|
|
66
|
+
for (const [, , x2, y2] of segs)
|
|
67
|
+
pts.push(`L ${rhu(x2)} ${rhu(y2)}`);
|
|
68
|
+
return pts.join(' ');
|
|
69
|
+
}
|
|
70
|
+
function classifyEdge(a, b, bends) {
|
|
71
|
+
const cxA = a.x + a.width / 2;
|
|
72
|
+
const cxB = b.x + b.width / 2;
|
|
73
|
+
const sameCol = Math.abs(cxA - cxB) < 8;
|
|
74
|
+
const isSkip = bends != null && bends.length > 0;
|
|
75
|
+
if (isSkip)
|
|
76
|
+
return sameCol ? 'skip-same-column' : 'skip-cross-column';
|
|
77
|
+
return sameCol ? 'direct-same-column' : 'direct-cross-column';
|
|
78
|
+
}
|
|
79
|
+
const MIN_PORT_GAP = 32;
|
|
80
|
+
const WALL_MARGIN = 16;
|
|
81
|
+
/**
|
|
82
|
+
* Cascade algorithm: given N ideal positions (pre-sorted ascending) in [lo, hi],
|
|
83
|
+
* return N positions that respect minGap while staying as close to ideals as possible.
|
|
84
|
+
* Falls back to even distribution when the required span exceeds the available space.
|
|
85
|
+
*/
|
|
86
|
+
function cascadePorts(ideals, lo, hi, minGap) {
|
|
87
|
+
const n = ideals.length;
|
|
88
|
+
if (n === 0)
|
|
89
|
+
return [];
|
|
90
|
+
if (n === 1)
|
|
91
|
+
return [Math.max(lo, Math.min(hi, ideals[0]))];
|
|
92
|
+
if ((n - 1) * minGap > hi - lo) {
|
|
93
|
+
const step = (hi - lo) / (n + 1);
|
|
94
|
+
return Array.from({ length: n }, (_, i) => lo + step * (i + 1));
|
|
95
|
+
}
|
|
96
|
+
const pos = ideals.map(v => Math.max(lo, Math.min(hi, v)));
|
|
97
|
+
for (let iter = 0; iter < 5; iter++) {
|
|
98
|
+
let changed = false;
|
|
99
|
+
for (let i = 1; i < n; i++) {
|
|
100
|
+
const minI = pos[i - 1] + minGap;
|
|
101
|
+
if (pos[i] < minI) {
|
|
102
|
+
pos[i] = minI;
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
107
|
+
const maxI = i === n - 1 ? hi : pos[i + 1] - minGap;
|
|
108
|
+
if (pos[i] > maxI) {
|
|
109
|
+
pos[i] = maxI;
|
|
110
|
+
changed = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (pos[0] < lo) {
|
|
114
|
+
pos[0] = lo;
|
|
115
|
+
changed = true;
|
|
116
|
+
}
|
|
117
|
+
if (!changed)
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
return pos;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Compute port points for one (box, wall) group of edges.
|
|
124
|
+
* Edges are sorted by their opposite-end node centre along the wall axis
|
|
125
|
+
* (crossing-minimisation), then spread via cascade to enforce MIN_PORT_GAP.
|
|
126
|
+
* Returns Map<relationIndex, {x,y}>.
|
|
127
|
+
*/
|
|
128
|
+
function assignGroupPorts(box, wall, group, yOff) {
|
|
129
|
+
const result = new Map();
|
|
130
|
+
if (group.length === 0)
|
|
131
|
+
return result;
|
|
132
|
+
const sorted = [...group].sort((a, b) => a.sourceCenter - b.sourceCenter);
|
|
133
|
+
let wallBase, wallLen, fixedCoord, isHorizontal;
|
|
134
|
+
switch (wall) {
|
|
135
|
+
case 'top':
|
|
136
|
+
wallBase = box.x;
|
|
137
|
+
wallLen = box.width;
|
|
138
|
+
fixedCoord = box.y + yOff;
|
|
139
|
+
isHorizontal = true;
|
|
140
|
+
break;
|
|
141
|
+
case 'bottom':
|
|
142
|
+
wallBase = box.x;
|
|
143
|
+
wallLen = box.width;
|
|
144
|
+
fixedCoord = box.y + yOff + box.height;
|
|
145
|
+
isHorizontal = true;
|
|
146
|
+
break;
|
|
147
|
+
case 'left':
|
|
148
|
+
wallBase = box.y + yOff;
|
|
149
|
+
wallLen = box.height;
|
|
150
|
+
fixedCoord = box.x;
|
|
151
|
+
isHorizontal = false;
|
|
152
|
+
break;
|
|
153
|
+
default: // 'right'
|
|
154
|
+
wallBase = box.y + yOff;
|
|
155
|
+
wallLen = box.height;
|
|
156
|
+
fixedCoord = box.x + box.width;
|
|
157
|
+
isHorizontal = false;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
const lo = wallBase + WALL_MARGIN;
|
|
161
|
+
const hi = wallBase + wallLen - WALL_MARGIN;
|
|
162
|
+
const ideals = sorted.map(e => Math.max(lo, Math.min(hi, e.sourceCenter)));
|
|
163
|
+
const positions = cascadePorts(ideals, lo, hi, MIN_PORT_GAP);
|
|
164
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
165
|
+
const p = positions[i];
|
|
166
|
+
result.set(sorted[i].ri, isHorizontal ? { x: p, y: fixedCoord } : { x: fixedCoord, y: p });
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
export function layoutClass(ir, theme) {
|
|
171
|
+
const { palette, typography, spacing } = theme;
|
|
172
|
+
const p = pen(theme);
|
|
173
|
+
const margin = spacing.diagramMargin;
|
|
174
|
+
const memFont = typography.smallFontSize;
|
|
175
|
+
const nameFont = typography.baseFontSize;
|
|
176
|
+
const lineH = rhuInt(memFont * 1.6);
|
|
177
|
+
const headH = rhuInt(nameFont * 1.6);
|
|
178
|
+
// ── Box sizing ─────────────────────────────────────────────────────────────
|
|
179
|
+
const sizeOf = (c) => {
|
|
180
|
+
const memberTexts = [...(c.stereotype ? [`«${c.stereotype}»`] : []), ...c.attributes.map(m => m.text), ...c.methods.map(m => m.text)];
|
|
181
|
+
const nameW = measureText(c.name, nameFont).width + 24;
|
|
182
|
+
const w = Math.max(130, nameW, ...memberTexts.map(t => measureText(t, memFont).width + 24));
|
|
183
|
+
const stereoH = c.stereotype ? memFont + 4 : 0;
|
|
184
|
+
const attrH = c.attributes.length * lineH + 8;
|
|
185
|
+
const methH = c.methods.length * lineH + 8;
|
|
186
|
+
const h = headH + stereoH + attrH + methH;
|
|
187
|
+
return { w, h, attrH: headH + stereoH + attrH };
|
|
188
|
+
};
|
|
189
|
+
const sizes = new Map(ir.classes.map(c => [c.name, sizeOf(c)]));
|
|
190
|
+
const nodes = ir.classes.map(c => ({ id: c.name, width: sizes.get(c.name).w, height: sizes.get(c.name).h }));
|
|
191
|
+
const edges = ir.relations.map(r => r.leftHead === 'triangle' ? { from: r.right, to: r.left } : { from: r.left, to: r.right });
|
|
192
|
+
const LAYER_GAP = 64;
|
|
193
|
+
const laid = layeredLayout(nodes, edges, { direction: 'TB', layerGap: LAYER_GAP, nodeGap: 46, margin });
|
|
194
|
+
const canvasWidth = laid.width + margin * 2;
|
|
195
|
+
const allRealBoxes = [...laid.boxes.values()];
|
|
196
|
+
// Segment registry: updated after each skip edge is routed.
|
|
197
|
+
const routedSegments = [];
|
|
198
|
+
// Real-node x-centre values, deduplicated and sorted.
|
|
199
|
+
const realColumnXs = [...new Set(allRealBoxes.map(b => b.x + b.width / 2))].sort((a, b) => a - b);
|
|
200
|
+
// Midpoint between each adjacent pair of real-node columns.
|
|
201
|
+
const interColMidpoints = [];
|
|
202
|
+
for (let ci = 0; ci < realColumnXs.length - 1; ci++) {
|
|
203
|
+
interColMidpoints.push((realColumnXs[ci] + realColumnXs[ci + 1]) / 2);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Score a candidate lane x for a skip or direct edge.
|
|
207
|
+
* Lower score = better. Returns Infinity if the candidate is out-of-bounds.
|
|
208
|
+
*/
|
|
209
|
+
function scoreLane(laneX, segments, interBoxes, routed, canvasW, realMinX, wallPairPenalty = 0, sameWallBonus = 0, straightBonus = 0, labelMid = null) {
|
|
210
|
+
if (laneX > canvasW)
|
|
211
|
+
return Infinity; // only hard-clip on the right
|
|
212
|
+
let pathLength = 0;
|
|
213
|
+
let segCount = segments.length;
|
|
214
|
+
let boxHits = 0;
|
|
215
|
+
let overlapHits = 0;
|
|
216
|
+
for (const [x1, y1, x2, y2] of segments) {
|
|
217
|
+
const dx = x2 - x1, dy = y2 - y1;
|
|
218
|
+
pathLength += Math.sqrt(dx * dx + dy * dy);
|
|
219
|
+
for (const nb of interBoxes) {
|
|
220
|
+
if (segmentIntersectsBox(x1, y1, x2, y2, nb))
|
|
221
|
+
boxHits++;
|
|
222
|
+
}
|
|
223
|
+
const segRect = toRect(x1, y1, x2, y2);
|
|
224
|
+
for (const rs of routed) {
|
|
225
|
+
if (rectsOverlapLength(segRect, rs) >= 10)
|
|
226
|
+
overlapHits++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const dirPenalty = laneX <= realMinX - CLEARANCE ? 0 : 5;
|
|
230
|
+
// Canvas expansion penalty: very small — we want comfortable spacing
|
|
231
|
+
// to win over tight spacing, not be penalised for it.
|
|
232
|
+
const expansionPenalty = laneX < realMinX ? (realMinX - laneX) * 0.05 : 0;
|
|
233
|
+
const labelPenalty = (labelMid != null && labelInBox(labelMid.x, labelMid.y, allRealBoxes))
|
|
234
|
+
? 200 : 0;
|
|
235
|
+
return (0.3 * pathLength +
|
|
236
|
+
10.0 * segCount +
|
|
237
|
+
1000 * boxHits +
|
|
238
|
+
50 * overlapHits +
|
|
239
|
+
dirPenalty +
|
|
240
|
+
expansionPenalty +
|
|
241
|
+
wallPairPenalty -
|
|
242
|
+
sameWallBonus - // bonus for same-wall same-column routing (B/C)
|
|
243
|
+
straightBonus + // reward for single-segment straight vertical
|
|
244
|
+
labelPenalty // penalty when label mid falls inside a box
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const title = ir.metadata.title;
|
|
248
|
+
const titleH = title ? typography.titleFontSize + 14 : 0;
|
|
249
|
+
const yOff = titleH;
|
|
250
|
+
const elements = [];
|
|
251
|
+
if (title)
|
|
252
|
+
elements.push(p.text(title, margin, margin + typography.titleFontSize, typography.titleFontSize, palette.text, { weight: 'bold' }));
|
|
253
|
+
// ── Relationships (under boxes) ────────────────────────────────────────────
|
|
254
|
+
const allBoxes = [...laid.boxes.values()];
|
|
255
|
+
const approachWall = (from, to) => {
|
|
256
|
+
if (from.y + from.height <= to.y)
|
|
257
|
+
return 'top';
|
|
258
|
+
if (to.y + to.height <= from.y)
|
|
259
|
+
return 'bottom';
|
|
260
|
+
const dx = (to.x + to.width / 2) - (from.x + from.width / 2);
|
|
261
|
+
const dy = (to.y + to.height / 2) - (from.y + from.height / 2);
|
|
262
|
+
if (Math.abs(dy) >= Math.abs(dx))
|
|
263
|
+
return dy >= 0 ? 'top' : 'bottom';
|
|
264
|
+
return dx >= 0 ? 'left' : 'right';
|
|
265
|
+
};
|
|
266
|
+
const wallPoint = (box, wall, t, yOff_) => {
|
|
267
|
+
const bx = box.x, by = box.y + yOff_, bw = box.width, bh = box.height;
|
|
268
|
+
switch (wall) {
|
|
269
|
+
case 'top': return { x: bx + t * bw, y: by };
|
|
270
|
+
case 'bottom': return { x: bx + t * bw, y: by + bh };
|
|
271
|
+
case 'left': return { x: bx, y: by + t * bh };
|
|
272
|
+
case 'right': return { x: bx + bw, y: by + t * bh };
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
// ── Port assignment: arrival ports (toPortMap2) ────────────────────────────
|
|
276
|
+
// Group edges by (targetId, wall). Sort each group by source-center along the
|
|
277
|
+
// wall axis → crossing-free order. Spread with cascade to enforce MIN_PORT_GAP.
|
|
278
|
+
// All edges (including skip edges) participate so ports are always separated.
|
|
279
|
+
// For skip edges, use laneX as the ideal arrival position so cascade sees a
|
|
280
|
+
// distinct ideal and doesn't tie with direct-edge ideals.
|
|
281
|
+
const toGroupAccum = new Map();
|
|
282
|
+
for (let ri = 0; ri < ir.relations.length; ri++) {
|
|
283
|
+
const r = ir.relations[ri];
|
|
284
|
+
const isFlipped = r.leftHead === 'triangle';
|
|
285
|
+
const a = laid.boxes.get(isFlipped ? r.right : r.left), b = laid.boxes.get(isFlipped ? r.left : r.right);
|
|
286
|
+
if (!a || !b)
|
|
287
|
+
continue;
|
|
288
|
+
const wall = approachWall(a, b);
|
|
289
|
+
const key = `${b.id}:${wall}`;
|
|
290
|
+
const bends = laid.edgeBends.get(ri);
|
|
291
|
+
const sourceCenter = (wall === 'top' || wall === 'bottom')
|
|
292
|
+
? (bends && bends.length > 0 ? bends[0].x : a.x + a.width / 2)
|
|
293
|
+
: a.y + a.height / 2 + yOff;
|
|
294
|
+
if (!toGroupAccum.has(key))
|
|
295
|
+
toGroupAccum.set(key, []);
|
|
296
|
+
toGroupAccum.get(key).push({ ri, sourceCenter });
|
|
297
|
+
}
|
|
298
|
+
const toPortMap2 = new Map();
|
|
299
|
+
for (const [key, group] of toGroupAccum) {
|
|
300
|
+
const nodeId = key.split(':')[0];
|
|
301
|
+
const wall = key.split(':')[1];
|
|
302
|
+
toPortMap2.set(key, assignGroupPorts(laid.boxes.get(nodeId), wall, group, yOff));
|
|
303
|
+
}
|
|
304
|
+
// ── Port assignment: departure ports (fromPortMap2) ────────────────────────
|
|
305
|
+
// Departure wall of A toward B = approachWall(b, a). Sort by target-center.
|
|
306
|
+
// For skip edges, use laneX as the ideal departure position so cascade sees a
|
|
307
|
+
// distinct ideal and doesn't tie with direct-edge ideals.
|
|
308
|
+
const fromGroupAccum = new Map();
|
|
309
|
+
for (let ri = 0; ri < ir.relations.length; ri++) {
|
|
310
|
+
const r = ir.relations[ri];
|
|
311
|
+
const isFlipped = r.leftHead === 'triangle';
|
|
312
|
+
const a = laid.boxes.get(isFlipped ? r.right : r.left), b = laid.boxes.get(isFlipped ? r.left : r.right);
|
|
313
|
+
if (!a || !b)
|
|
314
|
+
continue;
|
|
315
|
+
const wall = approachWall(b, a);
|
|
316
|
+
const key = `${a.id}:${wall}`;
|
|
317
|
+
const bends = laid.edgeBends.get(ri);
|
|
318
|
+
const targetCenter = (wall === 'top' || wall === 'bottom')
|
|
319
|
+
? (bends && bends.length > 0 ? bends[0].x : b.x + b.width / 2)
|
|
320
|
+
: b.y + b.height / 2 + yOff;
|
|
321
|
+
if (!fromGroupAccum.has(key))
|
|
322
|
+
fromGroupAccum.set(key, []);
|
|
323
|
+
fromGroupAccum.get(key).push({ ri, sourceCenter: targetCenter });
|
|
324
|
+
}
|
|
325
|
+
const fromPortMap2 = new Map();
|
|
326
|
+
for (const [key, group] of fromGroupAccum) {
|
|
327
|
+
const nodeId = key.split(':')[0];
|
|
328
|
+
const wall = key.split(':')[1];
|
|
329
|
+
fromPortMap2.set(key, assignGroupPorts(laid.boxes.get(nodeId), wall, group, yOff));
|
|
330
|
+
}
|
|
331
|
+
// Arrowhead direction: point just outside the wall in the edge's travel direction.
|
|
332
|
+
const wallDir = (wall, pt) => {
|
|
333
|
+
switch (wall) {
|
|
334
|
+
case 'top': return { x: pt.x, y: pt.y - 1 };
|
|
335
|
+
case 'bottom': return { x: pt.x, y: pt.y + 1 };
|
|
336
|
+
case 'left': return { x: pt.x - 1, y: pt.y };
|
|
337
|
+
case 'right': return { x: pt.x + 1, y: pt.y };
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const edgeEntries = [];
|
|
341
|
+
for (let ri_ = 0; ri_ < ir.relations.length; ri_++) {
|
|
342
|
+
const r_ = ir.relations[ri_];
|
|
343
|
+
const isFlipped_ = r_.leftHead === 'triangle';
|
|
344
|
+
const a_ = laid.boxes.get(isFlipped_ ? r_.right : r_.left), b_ = laid.boxes.get(isFlipped_ ? r_.left : r_.right);
|
|
345
|
+
if (!a_ || !b_)
|
|
346
|
+
continue;
|
|
347
|
+
const bends_ = laid.edgeBends.get(ri_);
|
|
348
|
+
const cls_ = classifyEdge(a_, b_, bends_);
|
|
349
|
+
const span_ = Math.abs((a_.x + a_.width / 2) - (b_.x + b_.width / 2)) +
|
|
350
|
+
Math.abs((a_.y + a_.height / 2) - (b_.y + b_.height / 2));
|
|
351
|
+
edgeEntries.push({ ri: ri_, cls: cls_, span: span_ });
|
|
352
|
+
}
|
|
353
|
+
const classOrder = {
|
|
354
|
+
'skip-cross-column': 0,
|
|
355
|
+
'skip-same-column': 1,
|
|
356
|
+
'direct-cross-column': 2,
|
|
357
|
+
'direct-same-column': 3,
|
|
358
|
+
};
|
|
359
|
+
edgeEntries.sort((ea, eb) => classOrder[ea.cls] - classOrder[eb.cls] || eb.span - ea.span);
|
|
360
|
+
for (const { ri, cls } of edgeEntries) {
|
|
361
|
+
const r = ir.relations[ri];
|
|
362
|
+
// When leftHead='triangle', the graph edge was flipped (from=r.right, to=r.left)
|
|
363
|
+
// for layout — rendering must match that direction.
|
|
364
|
+
const isFlipped = r.leftHead === 'triangle';
|
|
365
|
+
const a = laid.boxes.get(isFlipped ? r.right : r.left), b = laid.boxes.get(isFlipped ? r.left : r.right);
|
|
366
|
+
if (!a || !b)
|
|
367
|
+
continue;
|
|
368
|
+
// Arrival port: cascade-assigned position on target wall.
|
|
369
|
+
const toWall = approachWall(a, b);
|
|
370
|
+
const toPt = toPortMap2.get(`${b.id}:${toWall}`)?.get(ri) ?? wallPoint(b, toWall, 0.5, yOff);
|
|
371
|
+
// Departure port: cascade-assigned position on source wall, aimed at toPt.
|
|
372
|
+
const fromWall = approachWall(b, a);
|
|
373
|
+
const fromPt = fromPortMap2.get(`${a.id}:${fromWall}`)?.get(ri)
|
|
374
|
+
?? borderPoint({ ...a, y: a.y + yOff }, toPt.x, toPt.y);
|
|
375
|
+
const bends = laid.edgeBends.get(ri);
|
|
376
|
+
let safePath;
|
|
377
|
+
let labelMid;
|
|
378
|
+
let skipEdgeLateralStrategy = false;
|
|
379
|
+
let effectiveFromPt = fromPt;
|
|
380
|
+
let effectiveFromWall = fromWall;
|
|
381
|
+
let effectiveToPt = toPt;
|
|
382
|
+
let effectiveToWall = toWall;
|
|
383
|
+
if (bends && bends.length > 0) {
|
|
384
|
+
// ── Build candidate lane x positions ──────────────────────────────────
|
|
385
|
+
const srcLayerY = a.y;
|
|
386
|
+
const tgtLayerY = b.y;
|
|
387
|
+
const minY = Math.min(srcLayerY, tgtLayerY);
|
|
388
|
+
const maxY = Math.max(srcLayerY, tgtLayerY);
|
|
389
|
+
const interBoxes = allRealBoxes.filter(nb => {
|
|
390
|
+
const cy = nb.y + nb.height / 2;
|
|
391
|
+
return cy > minY && cy < maxY;
|
|
392
|
+
});
|
|
393
|
+
// Seed from BK sweeps: the 4 per-dummy x values from the first dummy in chain
|
|
394
|
+
const firstDummyId = laid.dummyChainIds.get(ri)?.[0] ?? null;
|
|
395
|
+
const sweepCandidates = firstDummyId
|
|
396
|
+
? (laid.dummySweepXs.get(firstDummyId) ?? [])
|
|
397
|
+
: [];
|
|
398
|
+
const rightMarginX = Math.max(...allRealBoxes.map(b => b.x + b.width)) + CLEARANCE;
|
|
399
|
+
const sourceX = fromPt.x;
|
|
400
|
+
const realMinX = Math.min(...allRealBoxes.map(b => b.x));
|
|
401
|
+
// ── Compute route for each candidate ──────────────────────────────────
|
|
402
|
+
const exitY = bends[0].y + yOff;
|
|
403
|
+
const entryY = toPt.y - LAYER_GAP / 2;
|
|
404
|
+
// Adaptive left-margin: push far enough left that horizontal segments at
|
|
405
|
+
// exitY and entryY clear ALL intermediate boxes.
|
|
406
|
+
// safeX = min(fromPt.x, toPt.x) - max(intermediate box right edge that
|
|
407
|
+
// would be crossed by a horizontal from laneX to fromPt.x) - CLEARANCE.
|
|
408
|
+
// Equivalently: laneX must satisfy laneX < min(interBox.left) for all
|
|
409
|
+
// interBoxes whose y range overlaps exitY or entryY.
|
|
410
|
+
const blockingAtExit = interBoxes.filter(b => exitY > b.y && exitY < b.y + b.height);
|
|
411
|
+
const blockingAtEntry = interBoxes.filter(b => entryY > b.y && entryY < b.y + b.height);
|
|
412
|
+
const allBlocking = [...blockingAtExit, ...blockingAtEntry];
|
|
413
|
+
const adaptiveLeftX = allBlocking.length > 0
|
|
414
|
+
? Math.min(...allBlocking.map(b => b.x)) - CLEARANCE
|
|
415
|
+
: realMinX - CLEARANCE;
|
|
416
|
+
const candidates_A = [
|
|
417
|
+
...sweepCandidates,
|
|
418
|
+
adaptiveLeftX,
|
|
419
|
+
rightMarginX,
|
|
420
|
+
sourceX,
|
|
421
|
+
...interColMidpoints,
|
|
422
|
+
];
|
|
423
|
+
// ── Mid-wall port coords for non-A strategies ──────────────────────────
|
|
424
|
+
const srcMidY_ = a.y + a.height / 2 + yOff;
|
|
425
|
+
const tgtMidY_ = b.y + b.height / 2 + yOff;
|
|
426
|
+
const srcLeft_ = a.x;
|
|
427
|
+
const srcRight_ = a.x + a.width;
|
|
428
|
+
const tgtLeft_ = b.x;
|
|
429
|
+
const tgtRight_ = b.x + b.width;
|
|
430
|
+
// Extended blocking set: covers B/C/D/E/F horizontal segments that may
|
|
431
|
+
// hit boxes at srcMidY or tgtMidY rows outside the strict inter-layer band.
|
|
432
|
+
const interBoxesExt = allRealBoxes.filter(nb => {
|
|
433
|
+
const cy = nb.y + nb.height / 2;
|
|
434
|
+
return cy > a.y && cy < b.y + b.height && nb.id !== a.id && nb.id !== b.id;
|
|
435
|
+
});
|
|
436
|
+
// Adaptive left-margin for B/D strategies (left-side lanes)
|
|
437
|
+
const blockingBD = allRealBoxes.filter(nb => {
|
|
438
|
+
const ny = nb.y, nh = nb.height;
|
|
439
|
+
return (srcMidY_ > ny && srcMidY_ < ny + nh) || (tgtMidY_ > ny && tgtMidY_ < ny + nh);
|
|
440
|
+
});
|
|
441
|
+
const adaptiveLeftX_BD = blockingBD.length > 0
|
|
442
|
+
? Math.min(...blockingBD.map(nb => nb.x)) - LANE_CLEARANCE
|
|
443
|
+
: realMinX - LANE_CLEARANCE;
|
|
444
|
+
// Adaptive right-margin for C/E strategies (right-side lanes)
|
|
445
|
+
const adaptiveRightX_CE = blockingBD.length > 0
|
|
446
|
+
? Math.max(...blockingBD.map(nb => nb.x + nb.width)) + LANE_CLEARANCE
|
|
447
|
+
: rightMarginX;
|
|
448
|
+
// Adaptive left-margin for F strategy (checks exitY and tgtMidY rows)
|
|
449
|
+
const blockingF = allRealBoxes.filter(nb => {
|
|
450
|
+
return (exitY > nb.y && exitY < nb.y + nb.height) ||
|
|
451
|
+
(tgtMidY_ > nb.y && tgtMidY_ < nb.y + nb.height);
|
|
452
|
+
});
|
|
453
|
+
const adaptiveLeftX_F = blockingF.length > 0
|
|
454
|
+
? Math.min(...blockingF.map(nb => nb.x)) - LANE_CLEARANCE
|
|
455
|
+
: realMinX - LANE_CLEARANCE;
|
|
456
|
+
// ── Segment builder functions ──────────────────────────────────────────
|
|
457
|
+
function buildSegmentsA(laneX) {
|
|
458
|
+
const fx = fromPt.x, fy = fromPt.y;
|
|
459
|
+
const tx = toPt.x, ty = toPt.y;
|
|
460
|
+
if (Math.abs(laneX - fx) < 1 && Math.abs(laneX - tx) < 1) {
|
|
461
|
+
return [[fx, fy, tx, ty]];
|
|
462
|
+
}
|
|
463
|
+
return [
|
|
464
|
+
[fx, fy, fx, exitY],
|
|
465
|
+
[fx, exitY, laneX, exitY],
|
|
466
|
+
[laneX, exitY, laneX, entryY],
|
|
467
|
+
[laneX, entryY, tx, entryY],
|
|
468
|
+
[tx, entryY, tx, ty],
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
function buildSegmentsB(laneX) {
|
|
472
|
+
return [
|
|
473
|
+
[srcLeft_, srcMidY_, laneX, srcMidY_],
|
|
474
|
+
[laneX, srcMidY_, laneX, tgtMidY_],
|
|
475
|
+
[laneX, tgtMidY_, tgtLeft_, tgtMidY_],
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
function buildSegmentsC(laneX) {
|
|
479
|
+
return [
|
|
480
|
+
[srcRight_, srcMidY_, laneX, srcMidY_],
|
|
481
|
+
[laneX, srcMidY_, laneX, tgtMidY_],
|
|
482
|
+
[laneX, tgtMidY_, tgtRight_, tgtMidY_],
|
|
483
|
+
];
|
|
484
|
+
}
|
|
485
|
+
function buildSegmentsD(laneX) {
|
|
486
|
+
return [
|
|
487
|
+
[srcLeft_, srcMidY_, laneX, srcMidY_],
|
|
488
|
+
[laneX, srcMidY_, laneX, entryY],
|
|
489
|
+
[laneX, entryY, toPt.x, entryY],
|
|
490
|
+
[toPt.x, entryY, toPt.x, toPt.y],
|
|
491
|
+
];
|
|
492
|
+
}
|
|
493
|
+
function buildSegmentsE(laneX) {
|
|
494
|
+
return [
|
|
495
|
+
[srcRight_, srcMidY_, laneX, srcMidY_],
|
|
496
|
+
[laneX, srcMidY_, laneX, entryY],
|
|
497
|
+
[laneX, entryY, toPt.x, entryY],
|
|
498
|
+
[toPt.x, entryY, toPt.x, toPt.y],
|
|
499
|
+
];
|
|
500
|
+
}
|
|
501
|
+
function buildSegmentsF(laneX) {
|
|
502
|
+
return [
|
|
503
|
+
[fromPt.x, fromPt.y, fromPt.x, exitY],
|
|
504
|
+
[fromPt.x, exitY, laneX, exitY],
|
|
505
|
+
[laneX, exitY, laneX, tgtMidY_],
|
|
506
|
+
[laneX, tgtMidY_, tgtLeft_, tgtMidY_],
|
|
507
|
+
];
|
|
508
|
+
}
|
|
509
|
+
// ── Build flat candidate list from all strategies ──────────────────────
|
|
510
|
+
const allCandidates = [];
|
|
511
|
+
for (const laneX of candidates_A) {
|
|
512
|
+
allCandidates.push({
|
|
513
|
+
strategy: 'A', laneX, isMixed: false,
|
|
514
|
+
segments: buildSegmentsA(laneX),
|
|
515
|
+
labelMid: { x: laneX, y: (exitY + entryY) / 2 },
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
const minAllowedLaneX = Math.min(srcLeft_, tgtLeft_) - LANE_CLEARANCE;
|
|
519
|
+
const laneXsB = [
|
|
520
|
+
adaptiveLeftX_BD, // LANE_CLEARANCE from nearest box
|
|
521
|
+
realMinX - LANE_CLEARANCE * 1.5,
|
|
522
|
+
realMinX - LANE_CLEARANCE * 2,
|
|
523
|
+
realMinX - LANE_CLEARANCE * 3,
|
|
524
|
+
...sweepCandidates,
|
|
525
|
+
].filter(x => x <= minAllowedLaneX); // must have at least LANE_CLEARANCE gap
|
|
526
|
+
for (const laneX of laneXsB) {
|
|
527
|
+
allCandidates.push({
|
|
528
|
+
strategy: 'B', laneX, isMixed: false,
|
|
529
|
+
segments: buildSegmentsB(laneX),
|
|
530
|
+
labelMid: { x: laneX, y: (srcMidY_ + tgtMidY_) / 2 },
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
const laneXsC = [
|
|
534
|
+
adaptiveRightX_CE,
|
|
535
|
+
rightMarginX,
|
|
536
|
+
rightMarginX + LANE_CLEARANCE * 0.5,
|
|
537
|
+
rightMarginX + LANE_CLEARANCE,
|
|
538
|
+
...sweepCandidates,
|
|
539
|
+
].filter(x => x > Math.max(srcRight_, tgtRight_));
|
|
540
|
+
for (const laneX of laneXsC) {
|
|
541
|
+
allCandidates.push({
|
|
542
|
+
strategy: 'C', laneX, isMixed: false,
|
|
543
|
+
segments: buildSegmentsC(laneX),
|
|
544
|
+
labelMid: { x: laneX, y: (srcMidY_ + tgtMidY_) / 2 },
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
const laneXsD = [adaptiveLeftX_BD, realMinX - CLEARANCE, ...sweepCandidates]
|
|
548
|
+
.filter(x => x < srcLeft_);
|
|
549
|
+
for (const laneX of laneXsD) {
|
|
550
|
+
allCandidates.push({
|
|
551
|
+
strategy: 'D', laneX, isMixed: true,
|
|
552
|
+
segments: buildSegmentsD(laneX),
|
|
553
|
+
labelMid: { x: laneX, y: (srcMidY_ + entryY) / 2 },
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
const laneXsE = [adaptiveRightX_CE, rightMarginX, ...sweepCandidates]
|
|
557
|
+
.filter(x => x > srcRight_);
|
|
558
|
+
for (const laneX of laneXsE) {
|
|
559
|
+
allCandidates.push({
|
|
560
|
+
strategy: 'E', laneX, isMixed: true,
|
|
561
|
+
segments: buildSegmentsE(laneX),
|
|
562
|
+
labelMid: { x: laneX, y: (srcMidY_ + entryY) / 2 },
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const laneXsF = [adaptiveLeftX_F, realMinX - CLEARANCE, ...sweepCandidates]
|
|
566
|
+
.filter(x => x < tgtLeft_);
|
|
567
|
+
for (const laneX of laneXsF) {
|
|
568
|
+
allCandidates.push({
|
|
569
|
+
strategy: 'F', laneX, isMixed: true,
|
|
570
|
+
segments: buildSegmentsF(laneX),
|
|
571
|
+
labelMid: { x: laneX, y: (exitY + tgtMidY_) / 2 },
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
// ── Score all candidates and pick best ────────────────────────────────
|
|
575
|
+
let bestScore = Infinity;
|
|
576
|
+
let bestCandidate = allCandidates[0] ?? {
|
|
577
|
+
strategy: 'A', laneX: bends[0].x, isMixed: false,
|
|
578
|
+
segments: buildSegmentsA(bends[0].x),
|
|
579
|
+
labelMid: { x: bends[0].x, y: (exitY + entryY) / 2 },
|
|
580
|
+
};
|
|
581
|
+
for (const c of allCandidates) {
|
|
582
|
+
// Same-wall bonus: Strategy B (left→left) and C (right→right) are
|
|
583
|
+
// the canonical routing style for skip edges in the same column.
|
|
584
|
+
// 30-point bonus ensures they win over A when geometry is clean.
|
|
585
|
+
const sameWallBonus = (c.strategy === 'B' || c.strategy === 'C') ? 30 : 0;
|
|
586
|
+
const score = scoreLane(c.laneX, c.segments, interBoxesExt, routedSegments, canvasWidth, realMinX, c.isMixed ? 2.0 : 0, sameWallBonus, 0, c.labelMid);
|
|
587
|
+
if (score < bestScore) {
|
|
588
|
+
bestScore = score;
|
|
589
|
+
bestCandidate = c;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
// ── Register winning segments ──────────────────────────────────────────
|
|
593
|
+
for (const [x1, y1, x2, y2] of bestCandidate.segments) {
|
|
594
|
+
routedSegments.push(toRect(x1, y1, x2, y2));
|
|
595
|
+
}
|
|
596
|
+
skipEdgeLateralStrategy = bestCandidate.strategy === 'B' || bestCandidate.strategy === 'C';
|
|
597
|
+
// ── Determine effective port points and walls for arrowheads ──────────
|
|
598
|
+
switch (bestCandidate.strategy) {
|
|
599
|
+
case 'B':
|
|
600
|
+
effectiveFromPt = { x: srcLeft_, y: srcMidY_ };
|
|
601
|
+
effectiveFromWall = 'left';
|
|
602
|
+
effectiveToPt = { x: tgtLeft_, y: tgtMidY_ };
|
|
603
|
+
effectiveToWall = 'left';
|
|
604
|
+
break;
|
|
605
|
+
case 'C':
|
|
606
|
+
effectiveFromPt = { x: srcRight_, y: srcMidY_ };
|
|
607
|
+
effectiveFromWall = 'right';
|
|
608
|
+
effectiveToPt = { x: tgtRight_, y: tgtMidY_ };
|
|
609
|
+
effectiveToWall = 'right';
|
|
610
|
+
break;
|
|
611
|
+
case 'D':
|
|
612
|
+
effectiveFromPt = { x: srcLeft_, y: srcMidY_ };
|
|
613
|
+
effectiveFromWall = 'left';
|
|
614
|
+
break;
|
|
615
|
+
case 'E':
|
|
616
|
+
effectiveFromPt = { x: srcRight_, y: srcMidY_ };
|
|
617
|
+
effectiveFromWall = 'right';
|
|
618
|
+
break;
|
|
619
|
+
case 'F':
|
|
620
|
+
effectiveToPt = { x: tgtLeft_, y: tgtMidY_ };
|
|
621
|
+
effectiveToWall = 'left';
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
// ── Render path from winning candidate's segments ─────────────────────
|
|
625
|
+
const segs = bestCandidate.segments;
|
|
626
|
+
if (segs.length === 1) {
|
|
627
|
+
safePath = `M ${rhu(segs[0][0])} ${rhu(segs[0][1])} L ${rhu(segs[0][2])} ${rhu(segs[0][3])}`;
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const pts = [`M ${rhu(segs[0][0])} ${rhu(segs[0][1])}`];
|
|
631
|
+
for (const [, , x2, y2] of segs) {
|
|
632
|
+
pts.push(`L ${rhu(x2)} ${rhu(y2)}`);
|
|
633
|
+
}
|
|
634
|
+
safePath = pts.join(' ');
|
|
635
|
+
}
|
|
636
|
+
labelMid = bestCandidate.labelMid;
|
|
637
|
+
elements.push(p.path(safePath, palette.textMuted, 1.3, r.dashed ? { dash: '6 4' } : {}));
|
|
638
|
+
// When edge is flipped, leftHead belongs at toPt (b=r.left) and rightHead at fromPt (a=r.right).
|
|
639
|
+
const headFrom = isFlipped ? r.rightHead : r.leftHead;
|
|
640
|
+
const headTo = isFlipped ? r.leftHead : r.rightHead;
|
|
641
|
+
elements.push(...endMarker(p, effectiveFromPt, wallDir(effectiveFromWall, effectiveFromPt), headFrom, palette));
|
|
642
|
+
elements.push(...endMarker(p, effectiveToPt, wallDir(effectiveToWall, effectiveToPt), headTo, palette));
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// ── Direct-edge optimizer (X1/X2 for cross-column; V/B/C for same-column) ──
|
|
646
|
+
const fx = fromPt.x, fy = fromPt.y, tx = toPt.x, ty = toPt.y;
|
|
647
|
+
const realMinX_d = Math.min(...allRealBoxes.map(nb => nb.x));
|
|
648
|
+
const rightMarginX_d = Math.max(...allRealBoxes.map(nb => nb.x + nb.width)) + CLEARANCE;
|
|
649
|
+
// All real boxes except source and target are obstacles.
|
|
650
|
+
const interBoxesDirect = allRealBoxes.filter(nb => nb.id !== a.id && nb.id !== b.id);
|
|
651
|
+
const directCandidates = [];
|
|
652
|
+
if (cls === 'direct-same-column') {
|
|
653
|
+
// V: straight vertical (or near-vertical) — gets a straightBonus
|
|
654
|
+
directCandidates.push({
|
|
655
|
+
strategy: 'V', laneX: fx, isMixed: false,
|
|
656
|
+
segments: [[fx, fy, tx, ty]],
|
|
657
|
+
labelMid: { x: fx, y: (fy + ty) / 2 },
|
|
658
|
+
});
|
|
659
|
+
// B/C: left/right wall lanes (same builders as skip edges)
|
|
660
|
+
const srcMidY_d = a.y + a.height / 2 + yOff;
|
|
661
|
+
const tgtMidY_d = b.y + b.height / 2 + yOff;
|
|
662
|
+
const srcLeft_d = a.x;
|
|
663
|
+
const srcRight_d = a.x + a.width;
|
|
664
|
+
const tgtLeft_d = b.x;
|
|
665
|
+
const tgtRight_d = b.x + b.width;
|
|
666
|
+
const blockingBD_d = allRealBoxes.filter(nb => {
|
|
667
|
+
const ny = nb.y, nh = nb.height;
|
|
668
|
+
return (srcMidY_d > ny && srcMidY_d < ny + nh) ||
|
|
669
|
+
(tgtMidY_d > ny && tgtMidY_d < ny + nh);
|
|
670
|
+
});
|
|
671
|
+
const adaptiveLeftX_BD_d = blockingBD_d.length > 0
|
|
672
|
+
? Math.min(...blockingBD_d.map(nb => nb.x)) - LANE_CLEARANCE
|
|
673
|
+
: realMinX_d - LANE_CLEARANCE;
|
|
674
|
+
const adaptiveRightX_CE_d = blockingBD_d.length > 0
|
|
675
|
+
? Math.max(...blockingBD_d.map(nb => nb.x + nb.width)) + LANE_CLEARANCE
|
|
676
|
+
: rightMarginX_d;
|
|
677
|
+
if (adaptiveLeftX_BD_d < srcLeft_d) {
|
|
678
|
+
directCandidates.push({
|
|
679
|
+
strategy: 'B', laneX: adaptiveLeftX_BD_d, isMixed: false,
|
|
680
|
+
segments: [
|
|
681
|
+
[srcLeft_d, srcMidY_d, adaptiveLeftX_BD_d, srcMidY_d],
|
|
682
|
+
[adaptiveLeftX_BD_d, srcMidY_d, adaptiveLeftX_BD_d, tgtMidY_d],
|
|
683
|
+
[adaptiveLeftX_BD_d, tgtMidY_d, tgtLeft_d, tgtMidY_d],
|
|
684
|
+
],
|
|
685
|
+
labelMid: { x: adaptiveLeftX_BD_d, y: (srcMidY_d + tgtMidY_d) / 2 },
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
if (adaptiveRightX_CE_d > srcRight_d) {
|
|
689
|
+
directCandidates.push({
|
|
690
|
+
strategy: 'C', laneX: adaptiveRightX_CE_d, isMixed: false,
|
|
691
|
+
segments: [
|
|
692
|
+
[srcRight_d, srcMidY_d, adaptiveRightX_CE_d, srcMidY_d],
|
|
693
|
+
[adaptiveRightX_CE_d, srcMidY_d, adaptiveRightX_CE_d, tgtMidY_d],
|
|
694
|
+
[adaptiveRightX_CE_d, tgtMidY_d, tgtRight_d, tgtMidY_d],
|
|
695
|
+
],
|
|
696
|
+
labelMid: { x: adaptiveRightX_CE_d, y: (srcMidY_d + tgtMidY_d) / 2 },
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
// direct-cross-column: X1 (V-then-H) and X2 (H-then-V) families
|
|
702
|
+
// ── X1 candidates ──
|
|
703
|
+
const midYRaw = [
|
|
704
|
+
(fy + ty) / 2,
|
|
705
|
+
fy + LAYER_GAP / 2,
|
|
706
|
+
ty - LAYER_GAP / 2,
|
|
707
|
+
];
|
|
708
|
+
for (const midY of dedup(midYRaw, 2)) {
|
|
709
|
+
const segsX1 = Math.abs(fx - tx) < 1
|
|
710
|
+
? [[fx, fy, tx, ty]]
|
|
711
|
+
: [[fx, fy, fx, midY], [fx, midY, tx, midY], [tx, midY, tx, ty]];
|
|
712
|
+
directCandidates.push({
|
|
713
|
+
strategy: 'X1', laneX: (fx + tx) / 2, isMixed: false,
|
|
714
|
+
segments: segsX1,
|
|
715
|
+
labelMid: { x: (fx + tx) / 2, y: midY },
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
// ── X2 candidates ──
|
|
719
|
+
const midXRaw = [
|
|
720
|
+
...interColMidpoints.filter(x => x > Math.min(fx, tx) && x < Math.max(fx, tx)),
|
|
721
|
+
realMinX_d - CLEARANCE,
|
|
722
|
+
rightMarginX_d,
|
|
723
|
+
];
|
|
724
|
+
for (const midX of dedup(midXRaw, 2)) {
|
|
725
|
+
const segsX2 = Math.abs(fy - ty) < 1
|
|
726
|
+
? [[fx, fy, tx, ty]]
|
|
727
|
+
: [[fx, fy, midX, fy], [midX, fy, midX, ty], [midX, ty, tx, ty]];
|
|
728
|
+
directCandidates.push({
|
|
729
|
+
strategy: 'X2', laneX: midX, isMixed: false,
|
|
730
|
+
segments: segsX2,
|
|
731
|
+
labelMid: { x: midX, y: (fy + ty) / 2 },
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
// ── Score all direct candidates and pick best ──────────────────────────
|
|
736
|
+
let bestScoreDirect = Infinity;
|
|
737
|
+
let bestDirect = directCandidates[0] ?? null;
|
|
738
|
+
for (const c of directCandidates) {
|
|
739
|
+
const straightBns = c.strategy === 'V' ? 40 : 0;
|
|
740
|
+
// For X1/X2 cross-column routes, use ALL boxes (including src/tgt)
|
|
741
|
+
// so segments that clip through the source or target body are penalised.
|
|
742
|
+
const boxesForScoring = (c.strategy === 'X1' || c.strategy === 'X2')
|
|
743
|
+
? allRealBoxes
|
|
744
|
+
: interBoxesDirect;
|
|
745
|
+
const score = scoreLane(c.laneX, c.segments, boxesForScoring, routedSegments, canvasWidth, realMinX_d, c.isMixed ? 2.0 : 0, 0, straightBns, c.labelMid);
|
|
746
|
+
if (score < bestScoreDirect) {
|
|
747
|
+
bestScoreDirect = score;
|
|
748
|
+
bestDirect = c;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (bestScoreDirect === Infinity || bestDirect == null || cls === 'direct-same-column') {
|
|
752
|
+
// Same-column direct edges: always use routeEdge() — cascade ports
|
|
753
|
+
// already handle port spreading; the V candidate deviates from them.
|
|
754
|
+
// Cross-column fallback when all candidates score Infinity.
|
|
755
|
+
const routed = routeEdge(a, b, allBoxes, yOff, fromPt, toPt, true);
|
|
756
|
+
safePath = routed.path || `M ${fromPt.x} ${fromPt.y} L ${toPt.x} ${toPt.y}`;
|
|
757
|
+
labelMid = routed.labelMidpoint;
|
|
758
|
+
// Register routeEdge segments so later edges see claimed corridors.
|
|
759
|
+
routedSegments.push(toRect(fromPt.x, fromPt.y, toPt.x, toPt.y));
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
// Register winning segments so later edges see claimed corridors.
|
|
763
|
+
for (const [x1, y1, x2, y2] of bestDirect.segments) {
|
|
764
|
+
routedSegments.push(toRect(x1, y1, x2, y2));
|
|
765
|
+
}
|
|
766
|
+
safePath = segmentsToPath(bestDirect.segments);
|
|
767
|
+
labelMid = bestDirect.labelMid;
|
|
768
|
+
}
|
|
769
|
+
elements.push(p.path(safePath, palette.textMuted, 1.3, r.dashed ? { dash: '6 4' } : {}));
|
|
770
|
+
elements.push(...endMarker(p, effectiveFromPt, wallDir(effectiveFromWall, effectiveFromPt), isFlipped ? r.rightHead : r.leftHead, palette));
|
|
771
|
+
elements.push(...endMarker(p, effectiveToPt, wallDir(effectiveToWall, effectiveToPt), isFlipped ? r.leftHead : r.rightHead, palette));
|
|
772
|
+
}
|
|
773
|
+
const mx = labelMid.x, my = labelMid.y;
|
|
774
|
+
if (r.label) {
|
|
775
|
+
let labelX, labelAnchor;
|
|
776
|
+
if (skipEdgeLateralStrategy && labelMid.x < Math.min(...allRealBoxes.map(b => b.x))) {
|
|
777
|
+
labelX = mx - 4;
|
|
778
|
+
labelAnchor = 'end';
|
|
779
|
+
}
|
|
780
|
+
else if (skipEdgeLateralStrategy) {
|
|
781
|
+
labelX = mx + 8;
|
|
782
|
+
labelAnchor = 'start';
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
labelX = mx;
|
|
786
|
+
labelAnchor = 'middle';
|
|
787
|
+
}
|
|
788
|
+
elements.push(p.text(r.label, rhuInt(labelX), rhuInt(my - 4), memFont, palette.textMuted, { anchor: labelAnchor }));
|
|
789
|
+
}
|
|
790
|
+
const cardOffset = (wall, pt) => {
|
|
791
|
+
switch (wall) {
|
|
792
|
+
case 'top': return { cx: pt.x + 10, cy: pt.y - 10 };
|
|
793
|
+
case 'bottom': return { cx: pt.x + 10, cy: pt.y + 10 };
|
|
794
|
+
case 'left': return { cx: pt.x - 10, cy: pt.y - 10 };
|
|
795
|
+
default: return { cx: pt.x + 10, cy: pt.y - 10 };
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
if (r.leftCard) {
|
|
799
|
+
const o = cardOffset(isFlipped ? effectiveToWall : effectiveFromWall, isFlipped ? effectiveToPt : effectiveFromPt);
|
|
800
|
+
elements.push(p.text(r.leftCard, rhu(o.cx), rhu(o.cy), memFont, palette.textMuted));
|
|
801
|
+
}
|
|
802
|
+
if (r.rightCard) {
|
|
803
|
+
const o = cardOffset(isFlipped ? effectiveFromWall : effectiveToWall, isFlipped ? effectiveFromPt : effectiveToPt);
|
|
804
|
+
elements.push(p.text(r.rightCard, rhu(o.cx), rhu(o.cy), memFont, palette.textMuted));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// ── Class boxes ────────────────────────────────────────────────────────────
|
|
808
|
+
for (const c of ir.classes) {
|
|
809
|
+
const box = laid.boxes.get(c.name);
|
|
810
|
+
const s = sizes.get(c.name);
|
|
811
|
+
const x = box.x, y = box.y + yOff;
|
|
812
|
+
elements.push(p.rect({ x: rhu(x), y: rhu(y), width: rhu(s.w), height: rhu(s.h) }, palette.surface, palette.border, 1.4, { rx: 4 }));
|
|
813
|
+
// Header — title vertically centered in headH band
|
|
814
|
+
const headerMidY = y + headH / 2;
|
|
815
|
+
let ty = headerMidY + nameFont * 0.35; // 0.35 ≈ half cap-height for baseline offset
|
|
816
|
+
elements.push(p.text(c.name, rhuInt(x + s.w / 2), rhu(ty), nameFont, palette.text, { weight: 'bold', anchor: 'middle' }));
|
|
817
|
+
if (c.stereotype) {
|
|
818
|
+
ty += memFont + 2;
|
|
819
|
+
elements.push(p.text(`«${c.stereotype}»`, rhuInt(x + s.w / 2), rhu(ty), memFont, palette.textMuted, { anchor: 'middle' }));
|
|
820
|
+
}
|
|
821
|
+
const headerBottom = y + headH + (c.stereotype ? memFont + 4 : 0);
|
|
822
|
+
elements.push(p.path(`M ${rhu(x)} ${rhu(headerBottom)} L ${rhu(x + s.w)} ${rhu(headerBottom)}`, palette.border, 1));
|
|
823
|
+
// Attributes
|
|
824
|
+
let ay = headerBottom + lineH - 4;
|
|
825
|
+
for (const m of c.attributes) {
|
|
826
|
+
elements.push(p.text(m.text, rhu(x + 8), rhu(ay), memFont, palette.text));
|
|
827
|
+
ay += lineH;
|
|
828
|
+
}
|
|
829
|
+
const attrBottom = y + s.attrH;
|
|
830
|
+
elements.push(p.path(`M ${rhu(x)} ${rhu(attrBottom)} L ${rhu(x + s.w)} ${rhu(attrBottom)}`, palette.border, 1));
|
|
831
|
+
// Methods
|
|
832
|
+
let my2 = attrBottom + lineH - 4;
|
|
833
|
+
for (const m of c.methods) {
|
|
834
|
+
elements.push(p.text(m.text, rhu(x + 8), rhu(my2), memFont, palette.text));
|
|
835
|
+
my2 += lineH;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
const totalW = rhuInt(laid.width + margin);
|
|
839
|
+
const totalH = rhuInt(laid.height + yOff + margin);
|
|
840
|
+
// If any skip-edge routing went left of x=margin (Strategy B lateral lanes),
|
|
841
|
+
// expand the viewBox leftward. Add extra margin for the label text width (~48px).
|
|
842
|
+
const LABEL_EXTRA = 48;
|
|
843
|
+
const routingMinX = Math.min(0, ...routedSegments.map(s => s.x1));
|
|
844
|
+
const leftOvershoot = routingMinX < margin ? Math.ceil(margin - routingMinX) + LABEL_EXTRA : 0;
|
|
845
|
+
const scene = applyOverlays({
|
|
846
|
+
viewBox: { x: -leftOvershoot, y: 0, width: totalW + leftOvershoot, height: totalH },
|
|
847
|
+
background: palette.background,
|
|
848
|
+
elements,
|
|
849
|
+
}, ir.overlays, theme);
|
|
850
|
+
return { scene, anchors: {} };
|
|
851
|
+
}
|
|
852
|
+
/** Draw a UML relationship end marker at `at`, pointing from `toward` into the box. */
|
|
853
|
+
function endMarker(p, at, toward, type, palette) {
|
|
854
|
+
if (type === 'none')
|
|
855
|
+
return [];
|
|
856
|
+
const ang = Math.atan2(at.y - toward.y, at.x - toward.x); // points outward from box centre toward marker
|
|
857
|
+
const back = ang + Math.PI; // into the box
|
|
858
|
+
const at2 = (len, spread) => ({
|
|
859
|
+
x: at.x + Math.cos(back + spread) * len,
|
|
860
|
+
y: at.y + Math.sin(back + spread) * len,
|
|
861
|
+
});
|
|
862
|
+
const tip = `${rhu(at.x)} ${rhu(at.y)}`;
|
|
863
|
+
if (type === 'arrow') {
|
|
864
|
+
const a = at2(12, 0.4), b = at2(12, -0.4);
|
|
865
|
+
return [p.path(`M ${rhu(a.x)} ${rhu(a.y)} L ${tip} L ${rhu(b.x)} ${rhu(b.y)}`, palette.textMuted, 1.4)];
|
|
866
|
+
}
|
|
867
|
+
if (type === 'triangle') {
|
|
868
|
+
const a = at2(14, 0.45), b = at2(14, -0.45);
|
|
869
|
+
return [p.path(`M ${tip} L ${rhu(a.x)} ${rhu(a.y)} L ${rhu(b.x)} ${rhu(b.y)} Z`, palette.textMuted, 1.3, { fill: palette.background })];
|
|
870
|
+
}
|
|
871
|
+
// diamonds
|
|
872
|
+
const near = at2(11, 0);
|
|
873
|
+
const a = at2(8, 0.9), b = at2(8, -0.9);
|
|
874
|
+
const fill = type === 'diamondF' ? palette.textMuted : palette.background;
|
|
875
|
+
return [p.path(`M ${tip} L ${rhu(a.x)} ${rhu(a.y)} L ${rhu(near.x)} ${rhu(near.y)} L ${rhu(b.x)} ${rhu(b.y)} Z`, palette.textMuted, 1.3, { fill })];
|
|
876
|
+
}
|
|
877
|
+
//# sourceMappingURL=layout.js.map
|