grid-router 0.1.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/corridors.d.ts +20 -0
- package/dist/corridors.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/layers.d.ts +39 -0
- package/dist/layers.js +36 -0
- package/dist/router.d.ts +109 -0
- package/dist/router.js +432 -0
- package/package.json +54 -0
- package/src/corridors.ts +29 -0
- package/src/index.ts +20 -0
- package/src/layers.ts +67 -0
- package/src/router.ts +519 -0
- package/src/svelte/GridDiagram.svelte +366 -0
- package/src/svelte/index.ts +21 -0
- package/src/svelte/types.ts +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Geptyro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# grid-router
|
|
2
|
+
|
|
3
|
+
Orthogonal edge routing on a grid for node-link diagrams: A\* with
|
|
4
|
+
per-direction cell occupancy, Steiner-style bus joins, and crossing "hops".
|
|
5
|
+
Framework-free core + an optional Svelte 5 canvas where **the consumer owns
|
|
6
|
+
the chips**.
|
|
7
|
+
|
|
8
|
+
## Concepts
|
|
9
|
+
|
|
10
|
+
- **Grid & occupancy** — the canvas is rasterized into square cells
|
|
11
|
+
(`res`, default 12px). Node boxes (inflated by `BOX_INFLATE`) hard-block
|
|
12
|
+
cells. Each free cell can host **one horizontal and one vertical** run;
|
|
13
|
+
same-direction reuse by another bus is practically prohibited, perpendicular
|
|
14
|
+
crossings pay a small toll. Lanes separate themselves; crossings happen only
|
|
15
|
+
where they're worth it.
|
|
16
|
+
- **Buses & joins** — edges sharing `(source, bus)` form a bus. Later edges of
|
|
17
|
+
a bus seed their search from every cell the bus already routed (cost ~0), so
|
|
18
|
+
branches leave the trunk at the optimal point. `exitCost` prices opening a
|
|
19
|
+
*new* trunk out of a source: raise it to force merges even over shorter
|
|
20
|
+
direct paths.
|
|
21
|
+
- **Endpoints are exclusive** — two buses never share a start/goal cell, so
|
|
22
|
+
stubs and arrowheads never stack.
|
|
23
|
+
- **Nothing is dropped** — if a dense node row walls the grid off, a desperate
|
|
24
|
+
pass routes through obstacles at a huge cost and the result counts it in
|
|
25
|
+
`violations`. **`violations > 0` is a layout problem, not a router bug**:
|
|
26
|
+
the layout does not supply enough corridor space.
|
|
27
|
+
- **Corridor supply** — `corridorGaps(res)` returns the empirically-validated
|
|
28
|
+
minimum `rowGap` / `chipGap` / `sidePad` the *layout* must provide so that
|
|
29
|
+
routing stays violation-free. A chip gap must fit one aligned free cell
|
|
30
|
+
column (`2·res + 2·inflate`); side padding keeps the outer columns routable.
|
|
31
|
+
- **Hops** — crossings are disambiguated without geometry: each bus paints a
|
|
32
|
+
background-colored halo under its paths, cutting a small gap into every bus
|
|
33
|
+
drawn before it (the cut line reads as tunneling under). Per-bus grouping
|
|
34
|
+
keeps a bus's own T-junctions intact.
|
|
35
|
+
- **Cost layers** — the router is deliberately agnostic of higher-level layout
|
|
36
|
+
concepts (lanes, sections, keep-out zones). `opts.costRegions` lets the
|
|
37
|
+
consumer express them anyway: rects with a per-cell `bias` — positive repels
|
|
38
|
+
routes, negative attracts them (e.g. a "lanes layer" biasing its corridors
|
|
39
|
+
negative). The step cost is floored, so negative biases are safe.
|
|
40
|
+
- **Port constraints** — `opts.nodeSides` restricts which box sides a node's
|
|
41
|
+
edges may attach to (schematic pins, side-anchored UML). For POSITIONED
|
|
42
|
+
named ports, register tiny pad elements at the pin locations as their own
|
|
43
|
+
nodes (`mcu:pb0`) and connect edges to those — the body registers separately
|
|
44
|
+
as a pure obstacle (see the circuit example); small boxes are first-class.
|
|
45
|
+
- **Pre-built layers** — `withLayers(base, portsLayer(…), keepOutLayer(…),
|
|
46
|
+
corridorLayer(…))` composes declarative routing facts into opts; falsy
|
|
47
|
+
entries are skipped so layers toggle inline
|
|
48
|
+
(`withLayers(base, zoneOn && keepOutLayer([zone]))`).
|
|
49
|
+
|
|
50
|
+
## Core API (framework-free)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { routeGrid, corridorGaps } from 'grid-router';
|
|
54
|
+
|
|
55
|
+
const result = routeGrid(
|
|
56
|
+
boxes, // Map<string, GridBox> — measured node rects (t/b/l/r/cx/cy)
|
|
57
|
+
edges, // { id, source, target, bus?, data? }[]
|
|
58
|
+
width, // routable canvas size in px
|
|
59
|
+
height,
|
|
60
|
+
{ res: 12, costOwn: 0.02, costTurn: 1, exitCost: 0 } // defaults shown
|
|
61
|
+
);
|
|
62
|
+
// result.conns: { id, source, target, bus, data, d /* SVG path */ }[]
|
|
63
|
+
// result.violations: number (0 = clean; see corridorGaps)
|
|
64
|
+
// result.bottom, result.debug (occupancy cells for overlays)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Svelte canvas
|
|
68
|
+
|
|
69
|
+
The component draws **only connectors**. You lay out arbitrary chip markup in
|
|
70
|
+
the default snippet and tag each connectable element with the provided
|
|
71
|
+
`register` action; chip events stay on your own DOM. Size the component's
|
|
72
|
+
container with CSS — it measures itself (no width/height props).
|
|
73
|
+
|
|
74
|
+
```svelte
|
|
75
|
+
<script lang="ts">
|
|
76
|
+
import { GridDiagram, type GridConn } from 'grid-router/svelte';
|
|
77
|
+
|
|
78
|
+
let hovered = $state<string | undefined>();
|
|
79
|
+
const edges = [{ id: 'e1', source: 'a', target: 'b', bus: 'ok', data: { tone: 'ok' } }];
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<GridDiagram
|
|
83
|
+
{edges}
|
|
84
|
+
opts={{ res: 12 }}
|
|
85
|
+
connStyle={(c) => ({
|
|
86
|
+
color: (c.data as { tone?: string })?.tone === 'ok' ? '#3fb950' : '#8b94a1',
|
|
87
|
+
dashed: false,
|
|
88
|
+
arrowAt: 'end',
|
|
89
|
+
class: hovered && hovered !== c.id ? 'dim' : ''
|
|
90
|
+
})}
|
|
91
|
+
onconnenter={(c) => (hovered = c.id)}
|
|
92
|
+
onconnleave={() => (hovered = undefined)}
|
|
93
|
+
onrouted={({ violations, ms }) => console.log(violations, ms)}
|
|
94
|
+
>
|
|
95
|
+
{#snippet children(register)}
|
|
96
|
+
<div class="my-layout">
|
|
97
|
+
<div use:register={'a'}>chip A</div>
|
|
98
|
+
<div use:register={'b'}>chip B</div>
|
|
99
|
+
</div>
|
|
100
|
+
{/snippet}
|
|
101
|
+
</GridDiagram>
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
/* consumer-driven highlight: connStyle classes land on package paths */
|
|
105
|
+
:global(path.dim) { opacity: 0.15; }
|
|
106
|
+
</style>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Layout contract: use the published CSS vars `--gr-row-gap` / `--gr-chip-gap`
|
|
110
|
+
(from `corridorGaps(res)`) as your row/chip gaps, and set `--grid-diagram-bg`
|
|
111
|
+
to your panel background so crossing hops cut with the right color.
|
|
112
|
+
|
|
113
|
+
## Examples
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
cd examples && npm install && npm run dev # http://localhost:5200
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- **Electronic circuit** — nets as buses (VCC/GND rails share trunks), no
|
|
120
|
+
arrowheads (`arrowAt: 'none'`), hover a wire to trace its whole net.
|
|
121
|
+
- **Org chart** — one bus per manager, reporting lines merge; hover a line to
|
|
122
|
+
light the whole team.
|
|
123
|
+
- **Pipeline + cost layers** — absolutely-positioned left→right DAG; toggle a
|
|
124
|
+
maintenance keep-out zone (`costRegions`) and watch routes detour, or hit
|
|
125
|
+
*randomize layout* — chips scatter and the router re-routes live (the
|
|
126
|
+
`revision` prop triggers re-measure when chips move without resizing the
|
|
127
|
+
canvas).
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
npm install
|
|
133
|
+
npm run build # tsc → dist (core)
|
|
134
|
+
npm test # vitest
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The Svelte entry ships as source (`src/svelte`) and is compiled by the
|
|
138
|
+
consumer's bundler (Vite + svelte plugin resolve the `svelte` export
|
|
139
|
+
condition).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Corridor supply rules — the layout side of the routing contract.
|
|
3
|
+
*
|
|
4
|
+
* The router can only use free cells; the LAYOUT decides how many exist. These
|
|
5
|
+
* formulas are the empirically-validated minimums (violations = 0 across cell
|
|
6
|
+
* sizes 6–12 on real diagrams):
|
|
7
|
+
*
|
|
8
|
+
* - rowGap: a gap between node rows must hold several free horizontal lanes
|
|
9
|
+
* after box inflation eats its borders;
|
|
10
|
+
* - chipGap: a gap between nodes in a row must fit at least one ALIGNED free
|
|
11
|
+
* cell column (2·res + 2·inflate), or a dense row walls the grid off;
|
|
12
|
+
* - sidePad: canvas side padding keeps the outer columns routable so paths
|
|
13
|
+
* can go AROUND a row instead of only through it.
|
|
14
|
+
*/
|
|
15
|
+
export interface CorridorGaps {
|
|
16
|
+
rowGap: number;
|
|
17
|
+
chipGap: number;
|
|
18
|
+
sidePad: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function corridorGaps(res: number, inflate?: number): CorridorGaps;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { routeGrid, BOX_INFLATE, type GridBox, type GridEdge, type GridConn, type GridOpts, type GridResult, type GridDebugCell, type BoxSide } from './router.js';
|
|
2
|
+
export { corridorGaps, type CorridorGaps } from './corridors.js';
|
|
3
|
+
export { withLayers, portsLayer, keepOutLayer, corridorLayer, type Rect, type RouterLayer } from './layers.js';
|
package/dist/index.js
ADDED
package/dist/layers.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BoxSide, GridOpts } from './router.js';
|
|
2
|
+
/**
|
|
3
|
+
* Layers: pre-built, composable producers of declarative routing facts.
|
|
4
|
+
*
|
|
5
|
+
* The router core stays agnostic — it only understands two substrates:
|
|
6
|
+
* per-cell cost bias (`costRegions`) and per-node port constraints
|
|
7
|
+
* (`nodeSides`). A layer is a small factory that expresses a CONSUMER concept
|
|
8
|
+
* (keep-out zone, preferred corridor, schematic pins…) in those substrates;
|
|
9
|
+
* `withLayers` merges any number of them into the opts.
|
|
10
|
+
*
|
|
11
|
+
* const opts = withLayers(
|
|
12
|
+
* { res: 12 },
|
|
13
|
+
* portsLayer({ r1: ['left', 'right'] }),
|
|
14
|
+
* maintenance && keepOutLayer([zoneRect])
|
|
15
|
+
* );
|
|
16
|
+
*/
|
|
17
|
+
export interface Rect {
|
|
18
|
+
l: number;
|
|
19
|
+
t: number;
|
|
20
|
+
r: number;
|
|
21
|
+
b: number;
|
|
22
|
+
}
|
|
23
|
+
export interface RouterLayer {
|
|
24
|
+
costRegions?: NonNullable<GridOpts['costRegions']>;
|
|
25
|
+
nodeSides?: Record<string, BoxSide[]>;
|
|
26
|
+
}
|
|
27
|
+
/** Merge layers into base opts. Falsy entries are skipped, so layers can be
|
|
28
|
+
* toggled inline: `withLayers(base, zoneOn && keepOutLayer([zone]))`. */
|
|
29
|
+
export declare function withLayers(base: GridOpts, ...layers: (RouterLayer | false | null | undefined)[]): GridOpts;
|
|
30
|
+
/** Port constraints: which box sides each node's edges may attach to
|
|
31
|
+
* (schematic pins, UML side-anchoring, …). */
|
|
32
|
+
export declare function portsLayer(sides: Record<string, BoxSide[]>): RouterLayer;
|
|
33
|
+
/** Keep-out zones: routes strongly avoid these rects (they still cross when
|
|
34
|
+
* there is no alternative — routing never fails). */
|
|
35
|
+
export declare function keepOutLayer(rects: Rect[], bias?: number): RouterLayer;
|
|
36
|
+
/** Preferred corridors: routes are gently attracted into these rects — the
|
|
37
|
+
* "lanes layer": express your layout's gaps as corridors and buses gravitate
|
|
38
|
+
* there. Negative bias is floored by the router, so any value is safe. */
|
|
39
|
+
export declare function corridorLayer(rects: Rect[], bias?: number): RouterLayer;
|
package/dist/layers.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Merge layers into base opts. Falsy entries are skipped, so layers can be
|
|
2
|
+
* toggled inline: `withLayers(base, zoneOn && keepOutLayer([zone]))`. */
|
|
3
|
+
export function withLayers(base, ...layers) {
|
|
4
|
+
const costRegions = [...(base.costRegions ?? [])];
|
|
5
|
+
const nodeSides = { ...(base.nodeSides ?? {}) };
|
|
6
|
+
for (const l of layers) {
|
|
7
|
+
if (!l)
|
|
8
|
+
continue;
|
|
9
|
+
if (l.costRegions)
|
|
10
|
+
costRegions.push(...l.costRegions);
|
|
11
|
+
if (l.nodeSides)
|
|
12
|
+
Object.assign(nodeSides, l.nodeSides);
|
|
13
|
+
}
|
|
14
|
+
const out = { ...base };
|
|
15
|
+
if (costRegions.length)
|
|
16
|
+
out.costRegions = costRegions;
|
|
17
|
+
if (Object.keys(nodeSides).length)
|
|
18
|
+
out.nodeSides = nodeSides;
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
/** Port constraints: which box sides each node's edges may attach to
|
|
22
|
+
* (schematic pins, UML side-anchoring, …). */
|
|
23
|
+
export function portsLayer(sides) {
|
|
24
|
+
return { nodeSides: sides };
|
|
25
|
+
}
|
|
26
|
+
/** Keep-out zones: routes strongly avoid these rects (they still cross when
|
|
27
|
+
* there is no alternative — routing never fails). */
|
|
28
|
+
export function keepOutLayer(rects, bias = 40) {
|
|
29
|
+
return { costRegions: rects.map((r) => ({ ...r, bias })) };
|
|
30
|
+
}
|
|
31
|
+
/** Preferred corridors: routes are gently attracted into these rects — the
|
|
32
|
+
* "lanes layer": express your layout's gaps as corridors and buses gravitate
|
|
33
|
+
* there. Negative bias is floored by the router, so any value is safe. */
|
|
34
|
+
export function corridorLayer(rects, bias = -0.4) {
|
|
35
|
+
return { costRegions: rects.map((r) => ({ ...r, bias })) };
|
|
36
|
+
}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orthogonal edge router on a grid.
|
|
3
|
+
*
|
|
4
|
+
* Rasterizes a canvas into square cells and routes every edge with A*:
|
|
5
|
+
*
|
|
6
|
+
* - node rects ("boxes", inflated) hard-block cells;
|
|
7
|
+
* - per-direction occupancy: a cell can host ONE horizontal and ONE vertical
|
|
8
|
+
* run — another bus reusing the same direction pays a near-prohibitive
|
|
9
|
+
* penalty (soft block, so routing never fails), while perpendicular
|
|
10
|
+
* crossings only pay a small toll → lanes separate themselves and crossings
|
|
11
|
+
* happen only where they're worth it;
|
|
12
|
+
* - same-bus discount + tree seeding: a bus's later edges branch from the
|
|
13
|
+
* OPTIMAL point of the already-routed trunk (Steiner-style joins), and
|
|
14
|
+
* `exitCost` prices opening a new trunk out of a source, so merges can win
|
|
15
|
+
* even over shorter direct paths;
|
|
16
|
+
* - turn penalty biases toward long straight runs;
|
|
17
|
+
* - endpoints are exclusive per bus (no stacked stubs/arrowheads);
|
|
18
|
+
* - an edge is NEVER dropped: when a node row walls the grid off, a desperate
|
|
19
|
+
* pass routes through obstacles at a huge cost and reports a violation.
|
|
20
|
+
*
|
|
21
|
+
* Pure geometry: the caller measures its DOM into {@link GridBox}es and
|
|
22
|
+
* renders the returned SVG path strings. Framework-free.
|
|
23
|
+
*/
|
|
24
|
+
export interface GridBox {
|
|
25
|
+
t: number;
|
|
26
|
+
b: number;
|
|
27
|
+
l: number;
|
|
28
|
+
r: number;
|
|
29
|
+
cx: number;
|
|
30
|
+
cy: number;
|
|
31
|
+
}
|
|
32
|
+
export interface GridEdge {
|
|
33
|
+
id: string;
|
|
34
|
+
source: string;
|
|
35
|
+
target: string;
|
|
36
|
+
/** Bus discriminator: edges sharing (source, bus) merge into one trunk
|
|
37
|
+
* tree. Defaults to the source alone. */
|
|
38
|
+
bus?: string;
|
|
39
|
+
/** Opaque consumer payload, passed through untouched onto the conn. */
|
|
40
|
+
data?: unknown;
|
|
41
|
+
}
|
|
42
|
+
export interface GridConn {
|
|
43
|
+
id: string;
|
|
44
|
+
source: string;
|
|
45
|
+
target: string;
|
|
46
|
+
/** Full bus key (`source|bus`) — stable grouping handle for rendering
|
|
47
|
+
* (e.g. crossing-hop halos are painted per bus). */
|
|
48
|
+
bus: string;
|
|
49
|
+
data?: unknown;
|
|
50
|
+
/** Orthogonal SVG path: source contact → routed cells → target contact. */
|
|
51
|
+
d: string;
|
|
52
|
+
}
|
|
53
|
+
export interface GridDebugCell {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
kind: 'chip' | 'h' | 'v' | 'hv' | 'bad';
|
|
57
|
+
}
|
|
58
|
+
export interface GridResult {
|
|
59
|
+
conns: GridConn[];
|
|
60
|
+
/** deepest y any path reaches (callers reserve layout height for it) */
|
|
61
|
+
bottom: number;
|
|
62
|
+
/** lane-sharing incidents (should be 0 — non-zero means the layout does
|
|
63
|
+
* not supply enough corridor space, see corridorGaps()) */
|
|
64
|
+
violations: number;
|
|
65
|
+
debug: {
|
|
66
|
+
res: number;
|
|
67
|
+
cols: number;
|
|
68
|
+
rows: number;
|
|
69
|
+
cells: GridDebugCell[];
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Box inflation in px: routed runs keep this clearance off every box. */
|
|
73
|
+
export declare const BOX_INFLATE = 3;
|
|
74
|
+
export interface GridOpts {
|
|
75
|
+
/** grid cell size in px (default 12) */
|
|
76
|
+
res?: number;
|
|
77
|
+
/** cost per cell when riding cells the bus already owns (default 0.02) */
|
|
78
|
+
costOwn?: number;
|
|
79
|
+
/** penalty per 90° turn (default 1) */
|
|
80
|
+
costTurn?: number;
|
|
81
|
+
/** penalty for opening a NEW trunk out of a source (default 0) — raise it
|
|
82
|
+
* to make a bus's edges merge even when direct paths would be shorter */
|
|
83
|
+
exitCost?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Cost layers: arbitrary rects (px) adding a per-cell bias to every step
|
|
86
|
+
* inside them — positive repels routes, negative attracts them. This is
|
|
87
|
+
* the generic hook for consumer concepts the router deliberately does not
|
|
88
|
+
* know about (e.g. a "lanes" layer biasing corridors negative, keep-out
|
|
89
|
+
* zones, preferred highways). The effective step cost is floored so
|
|
90
|
+
* negative biases can't break A*.
|
|
91
|
+
*/
|
|
92
|
+
costRegions?: {
|
|
93
|
+
l: number;
|
|
94
|
+
t: number;
|
|
95
|
+
r: number;
|
|
96
|
+
b: number;
|
|
97
|
+
bias: number;
|
|
98
|
+
}[];
|
|
99
|
+
/**
|
|
100
|
+
* Port constraints: restrict which box sides edges may attach to, per node
|
|
101
|
+
* id (schematic semantics — a horizontal resistor only connects via its
|
|
102
|
+
* left/right leads, an IC via its pin sides). Unlisted nodes accept all
|
|
103
|
+
* four sides. The desperate fallback relaxes the constraint rather than
|
|
104
|
+
* dropping the edge.
|
|
105
|
+
*/
|
|
106
|
+
nodeSides?: Record<string, BoxSide[]>;
|
|
107
|
+
}
|
|
108
|
+
export type BoxSide = 'top' | 'bottom' | 'left' | 'right';
|
|
109
|
+
export declare function routeGrid(boxes: Map<string, GridBox>, edges: GridEdge[], width: number, height: number, opts?: GridOpts): GridResult;
|