@weasel-js/diagram 1.10.1 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/layout.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { k as BodyFloor, l as BodyNodeSpec, B as BodySpec, m as Bounds, n as BuildBodyOptions, C as COMPASS, q as DEFAULT_NODE_GAP, r as DEFAULT_PORTS, s as DEFAULT_RANK_GAP, f as DiagramNode, v as ForceBody, F as ForceOptions, w as ForceRelaxation, i as Graph, y as GraphEdge, A as GraphNode, I as LayoutAxes, j as LayoutDirection, L as LayoutFn, g as LayoutOptions, h as LayoutResult, M as MeasureRowText, O as Outline, P as Port, c as PortAnchor, e as PortSpec, a as PortsOptions, R as Row, J as RowBox, K as RowNodeData, N as RowPort, Q as RowPortBox, S as RowTextStyle, T as Slot, U as axesFor, V as backEdges, W as bodyOutline, Y as boxForContent, Z as buildBody, $ as canvasMeasure, a0 as contentBox, aw as extent, a5 as force, a6 as forceRelaxation, a7 as forestOf, a8 as graphOrder, ab as layered, ac as layoutBody, ad as layoutRowPorts, ae as measureBody, af as outlinePath, ag as outlinePolyline, ah as packAcross, aj as pinnedSet, ak as portOf, al as portsOf, am as portsOnOutline, ao as ranksOf, ap as rayHit, aq as seededOrder, ar as settle, as as sizeToBody, at as translated, au as tree, av as untilSettled } from './layout.entry-BUkiRKuX.js';
2
- import '@weasel-js/geom';
1
+ export { t as BodyFloor, u as BodyNodeSpec, B as BodySpec, m as Bounds, v as BuildBodyOptions, y as DEFAULT_HOP_SIZE, z as DEFAULT_NODE_GAP, A as DEFAULT_RANK_GAP, c as DiagramLabel, f as DiagramNode, E as EdgeHop, J as EdgeLabelRoom, G as Graph, K as GraphEdge, N as GraphGroup, Q as GraphNode, U as HOPS_DOC_KEY, V as HopsDocValue, l as LabelSpot, X as LayoutAxes, q as LayoutDirection, d as LayoutFn, e as LayoutOptions, r as LayoutPlan, p as LayoutPlanner, L as LayoutPorts, n as LayoutResult, M as MeasureRowText, O as Outline, Y as PORT_LABEL_GAP, Z as PerimeterPortLabelBox, g as Port, P as PortAnchor, $ as PortSite, b as PortSpec, a as Row, a0 as RowBox, a1 as RowNodeData, a2 as RowPort, a3 as RowPortBox, R as RowTextStyle, a4 as Slot, a5 as anchorOffset, a6 as axesFor, a8 as bodyOutline, aa as boxForContent, ab as buildBody, ad as canvasMeasure, ae as contentBox, aI as extent, al as graphOrder, ap as layoutBody, aq as layoutRowPorts, ar as marginOf, as as measureBody, at as outlinePath, au as packAcross, aw as pinnedSet, ax as planOf, ay as plannerPlan, az as portLabelBox, aC as seededOrder, aD as settle, aE as sizeToBody, aG as translated, aH as untilSettled } from './types-BbHunLXm.js';
2
+ export { e as AVOID_DEFAULTS, f as AvoidEdge, g as AvoidEnd, A as AvoidInput, h as AvoidLabel, i as AvoidObstacle, b as AvoidOptions, j as AvoidPort, a as AvoidRouter, c as AvoidRoutes, k as AvoidUpdateOptions, C as COMPASS, D as DEFAULT_PORTS, l as ForceBody, F as ForceOptions, m as ForceRelaxation, L as LabelPlacements, o as LayeredPlan, p as PlaceLabel, q as PlaceLabelsInput, d as PlaceLabelsOptions, r as PlaceRoute, P as PortsOptions, s as backEdges, t as chain, u as createAvoidRouter, v as edgeHops, w as footprintNode, x as force, y as forceRelaxation, z as forestOf, B as labelCenter, E as layered, G as layeredPlan, H as outlinePolyline, I as placeLabels, J as portOf, K as portSitesOf, M as portsOf, N as portsOnOutline, O as ranksByLength, Q as ranksOf, R as rayHit, S as routeHolds, T as tree } from './layout.entry-CH9gc0CY.js';
3
3
  import '@weasel-js/core/math';
4
+ import '@weasel-js/geom';
4
5
  import '@weasel-js/simulation';
@@ -0,0 +1,512 @@
1
+ import { Q as GraphNode, i as DiagramNodeReader, b as PortSpec, h as DiagramNodeLike, g as Port, c as DiagramLabel, m as Bounds, E as EdgeHop, l as LabelSpot, e as LayoutOptions, G as Graph, n as LayoutResult, d as LayoutFn, L as LayoutPorts, O as Outline } from './types-BbHunLXm.js';
2
+ import { SimulationNode, createSimulation } from '@weasel-js/simulation';
3
+ import { PoseDescriptor, Vec2, Path } from '@weasel-js/core/math';
4
+
5
+ /** `node` as layout sees it: bounds grown by its margin, ports where they were. */
6
+ declare function footprintNode(node: GraphNode): GraphNode;
7
+
8
+ /**
9
+ * Where a node's ports are, in world coordinates.
10
+ *
11
+ * A port is placed in the node's **unrotated** bounds, then carried through
12
+ * the pose's rotation — the same pivot every other kit reader uses, the AABB
13
+ * center. Bounds come from the pose descriptor rather than from `pose.width`
14
+ * and friends, so a node whose pose is a path, an ellipse or anything else a
15
+ * consumer wired is connectable on the same terms as a rect.
16
+ */
17
+
18
+ /** The eight anchors almost everything wants, named the way a compass is. */
19
+ declare const COMPASS: {
20
+ readonly nw: {
21
+ readonly u: 0;
22
+ readonly v: 0;
23
+ };
24
+ readonly n: {
25
+ readonly u: 0.5;
26
+ readonly v: 0;
27
+ };
28
+ readonly ne: {
29
+ readonly u: 1;
30
+ readonly v: 0;
31
+ };
32
+ readonly e: {
33
+ readonly u: 1;
34
+ readonly v: 0.5;
35
+ };
36
+ readonly se: {
37
+ readonly u: 1;
38
+ readonly v: 1;
39
+ };
40
+ readonly s: {
41
+ readonly u: 0.5;
42
+ readonly v: 1;
43
+ };
44
+ readonly sw: {
45
+ readonly u: 0;
46
+ readonly v: 1;
47
+ };
48
+ readonly w: {
49
+ readonly u: 0;
50
+ readonly v: 0.5;
51
+ };
52
+ };
53
+ /**
54
+ * What a node carrying no `ports` gets: the four edge midpoints of its own
55
+ * bounds. Every node has bounds, so every node is connectable without saying
56
+ * anything.
57
+ */
58
+ declare const DEFAULT_PORTS: readonly PortSpec[];
59
+ /** How ports are resolved against a node: its bounds, and its trait. */
60
+ interface PortsOptions<TPose> {
61
+ /** Reads the AABB a port is placed in, and the node's rotation. Defaults to
62
+ * the kit's `AUTO_POSE_DESCRIPTOR`, which handles rect and path poses. */
63
+ geometry?: PoseDescriptor<TPose>;
64
+ /** How the `DiagramNode` trait is read off a node. */
65
+ read?: DiagramNodeReader;
66
+ }
67
+ /**
68
+ * Every port on `node`, resolved against `pose`.
69
+ *
70
+ * Pass the pose the node is **painted** at — `effectivePose(scene, node)` —
71
+ * not `node.pose`. A port that answers from the document pose while the node
72
+ * is mid-drag puts an edge somewhere the user can see the node is not.
73
+ */
74
+ declare function portsOf<TPose>(node: DiagramNodeLike, pose: TPose, opts?: PortsOptions<TPose>): Port[];
75
+ /** One named port, or `undefined` when the node has no such port. */
76
+ declare function portOf<TPose>(node: DiagramNodeLike, pose: TPose, portId: string, opts?: PortsOptions<TPose>): Port | undefined;
77
+ /** One named port at each place it may sit: at `at` first, then at each of
78
+ * its `sites`. Empty when the node has no such port. */
79
+ declare function portSitesOf<TPose>(node: DiagramNodeLike, pose: TPose, portId: string, opts?: PortsOptions<TPose>): Port[];
80
+
81
+ /**
82
+ * What the obstacle-avoiding router reads and answers. Plain data: it knows
83
+ * nothing of scenes, so a host with its own nodes routes with it directly.
84
+ */
85
+
86
+ /** An axis-aligned box in world units. */
87
+ type Box = Bounds;
88
+ /** A place a connector's end may leave or arrive: a port's point and the
89
+ * unit direction it faces away from its shape. */
90
+ interface AvoidPort {
91
+ point: Vec2;
92
+ normal: Vec2;
93
+ }
94
+ /** One end of a connector: the shape it is on, and the ports it may use.
95
+ * An end glued to a shape rather than to one port offers every port. */
96
+ interface AvoidEnd {
97
+ node: string;
98
+ ports: readonly AvoidPort[];
99
+ }
100
+ /** A connector to route. */
101
+ interface AvoidEdge {
102
+ id: string;
103
+ from: AvoidEnd;
104
+ to: AvoidEnd;
105
+ /** The author's stops between the ends, passed in order. */
106
+ waypoints?: readonly Vec2[];
107
+ /** The route it holds now: the bends between its ends. */
108
+ route?: readonly Vec2[];
109
+ /** Its label, which every other connector routes around. The router
110
+ * stations it on this connector's route as `labelCenter` does. */
111
+ label?: AvoidLabel;
112
+ }
113
+ /** A connector's label as the router reads it: where along the route it
114
+ * sits, and how big it is. */
115
+ interface AvoidLabel {
116
+ id: string;
117
+ placement: DiagramLabel;
118
+ size: {
119
+ width: number;
120
+ height: number;
121
+ };
122
+ }
123
+ /**
124
+ * Something connectors route around.
125
+ *
126
+ * - `shape` blocks every connector beyond its own port stubs.
127
+ * - `region` (a container or pool) blocks a connector only when neither of
128
+ * its ends is one of `members`.
129
+ * - `label` blocks every connector but `edge`, the one it labels.
130
+ */
131
+ interface AvoidObstacle {
132
+ id: string;
133
+ box: Box;
134
+ kind: 'shape' | 'region' | 'label';
135
+ /** For a region: the shapes inside it, at any depth. */
136
+ members?: ReadonlySet<string>;
137
+ /** For a label: the connector it labels. */
138
+ edge?: string;
139
+ }
140
+ /** What one update reads: every obstacle and every connector there is now. */
141
+ interface AvoidInput {
142
+ obstacles: readonly AvoidObstacle[];
143
+ edges: readonly AvoidEdge[];
144
+ }
145
+ /** How the router routes. Every field has a default. */
146
+ interface AvoidOptions {
147
+ /** How far a route keeps from every obstacle, in world units. Default 10. */
148
+ clearance?: number;
149
+ /** Cost of one turn, in world units of length. Default 40. */
150
+ bendPenalty?: number;
151
+ /** Cost of crossing another connector's route. Default 20. */
152
+ crossPenalty?: number;
153
+ /** Distance between parallel runs after nudging. Default 8. */
154
+ spacing?: number;
155
+ /** How far past a leg's stops the search first looks; it doubles while no
156
+ * path fits. Default 100. */
157
+ margin?: number;
158
+ /** How far a route keeps from another connector's label. Default 4. */
159
+ labelClearance?: number;
160
+ }
161
+ /** {@link AvoidOptions} with every default filled in. */
162
+ type ResolvedAvoidOptions = Required<AvoidOptions>;
163
+ /** The defaults behind {@link AvoidOptions}. */
164
+ declare const AVOID_DEFAULTS: ResolvedAvoidOptions;
165
+
166
+ /**
167
+ * The incremental router: it holds the obstacles it last committed and the
168
+ * routes it last made, and each update reroutes only what changed.
169
+ */
170
+
171
+ /** How one {@link AvoidRouter.update} runs. */
172
+ interface AvoidUpdateOptions {
173
+ /** A drag frame: route against the committed obstacles without replacing
174
+ * them, with no crossing penalty and no nudging. */
175
+ preview?: boolean;
176
+ /** Milliseconds a preview may spend. The connectors it does not reach keep
177
+ * the route the last preview gave them. Default no limit. */
178
+ budgetMs?: number;
179
+ }
180
+ /** Each rerouted connector's new route, the bends between its ends, or null
181
+ * where no path was found and the connector falls back to `orthogonal`. */
182
+ type AvoidRoutes = Map<string, Vec2[] | null>;
183
+ /** An obstacle-avoiding router that remembers what it last routed. */
184
+ interface AvoidRouter {
185
+ /** Take `input` as already routed: its obstacles as committed and each
186
+ * connector's `route` as its own, routing nothing. */
187
+ seed(input: AvoidInput): void;
188
+ /** Reroute what changed since the last commit, and commit `input` unless
189
+ * this is a preview. Answers only the connectors it rerouted, or moved
190
+ * while spreading a channel they share. */
191
+ update(input: AvoidInput, opts?: AvoidUpdateOptions): AvoidRoutes;
192
+ }
193
+ /** A router with `options` over the defaults. */
194
+ declare function createAvoidRouter(options?: AvoidOptions): AvoidRouter;
195
+
196
+ /**
197
+ * Whether a stored route still fits its ends: it leaves along the departing
198
+ * port's facing, arrives against the receiving one's, and passes every
199
+ * authored waypoint in order. One that does not was made for ends that have
200
+ * since moved.
201
+ */
202
+
203
+ /** An end as the check reads it: where it is and which way it faces. */
204
+ interface HeldEnd {
205
+ point: Vec2;
206
+ normal: Vec2 | null;
207
+ }
208
+ /** Whether `route`, the bends between `from` and `to`, still fits them and passes `waypoints` in order. */
209
+ declare function routeHolds(from: HeldEnd, to: HeldEnd, waypoints: readonly Vec2[], route: readonly Vec2[]): boolean;
210
+
211
+ /**
212
+ * Where connectors cross, as the hops the upper one of each pair makes over
213
+ * the lower. Arithmetic on polylines: nothing here reads a scene.
214
+ */
215
+
216
+ /**
217
+ * The hops each connector makes where it crosses one drawn before it.
218
+ *
219
+ * `polylines` holds each connector's route by id; `order` is render order, and
220
+ * of two crossing connectors the later one hops. Only proper crossings count:
221
+ * a touch at an end and a collinear overlap are not, nor is a crossing within
222
+ * `2r` of either connector's ends, nor one within `r` of a bend of the
223
+ * hopping connector, where its half circle would not fit. Hops on one segment
224
+ * closer than `2r` merge into one longer hop.
225
+ *
226
+ * Answers every connector of `only` (default: all of `order`), with no hops
227
+ * as an empty list.
228
+ */
229
+ declare function edgeHops(polylines: ReadonlyMap<string, readonly Vec2[]>, order: readonly string[], r: number, only?: ReadonlySet<string>): Map<string, EdgeHop[]>;
230
+
231
+ /**
232
+ * Where a label stands on a route: the arithmetic `labelDerivePose` runs,
233
+ * kept free of the scene so the router and the placer can station a label
234
+ * without one.
235
+ */
236
+
237
+ /** Where the center of a label `size` big sits along `whole`, on its first
238
+ * subpath, at the station placement chose for it when it has one; `null`
239
+ * for a path with no points. */
240
+ declare function labelCenter(whole: Path, label: DiagramLabel, size: {
241
+ width: number;
242
+ height: number;
243
+ }): Vec2 | null;
244
+
245
+ /**
246
+ * Label placement as arithmetic: shapes, routes, and labels in, the station
247
+ * each moving label settles at out. A label that something covers slides
248
+ * along its own route, or flips to the other side of it, to the nearest
249
+ * clear spot.
250
+ */
251
+
252
+ /** A connector's route as placement reads it: every point it runs through,
253
+ * its ends included. */
254
+ interface PlaceRoute {
255
+ id: string;
256
+ points: readonly Vec2[];
257
+ /** Further runs it draws, apart from `points`: the far stub of a connector
258
+ * split across pages. Other labels keep off them; its own stands on `points`. */
259
+ more?: readonly (readonly Vec2[])[];
260
+ }
261
+ /** A label to place, or to steer the others off. */
262
+ interface PlaceLabel {
263
+ id: string;
264
+ /** The connector it sits on: a {@link PlaceRoute}'s id. */
265
+ edge: string;
266
+ /** Its trait. `at` and `offset` are the preferred station; `placed`, where
267
+ * it stands now; `dodge: false`, never move it. */
268
+ placement: DiagramLabel;
269
+ size: {
270
+ width: number;
271
+ height: number;
272
+ };
273
+ /** Whether it may move now. Default true. One that may not is an
274
+ * obstacle where it stands. */
275
+ moving?: boolean;
276
+ }
277
+ /** What one placement reads. */
278
+ interface PlaceLabelsInput {
279
+ shapes: readonly {
280
+ id: string;
281
+ box: Bounds;
282
+ }[];
283
+ routes: readonly PlaceRoute[];
284
+ labels: readonly PlaceLabel[];
285
+ }
286
+ /** How labels are placed. Every field has a default. */
287
+ interface PlaceLabelsOptions {
288
+ /** How far a label keeps from what it dodges, in world units. Default 2. */
289
+ gap?: number;
290
+ /** Stations tried each way along the route. Default 4. */
291
+ tries?: number;
292
+ /** Room between one station and the next, beyond the label's width. Default 8. */
293
+ spacing?: number;
294
+ }
295
+ /** Where a moving label settles: a station off its preference, or null when
296
+ * its preferred station is the best there is. */
297
+ type LabelPlacements = Map<string, LabelSpot | null>;
298
+ /**
299
+ * Where each moving label settles. Each tries its preferred station, then
300
+ * the stations either side of it along its route and across the line, and
301
+ * keeps the cheapest: area over shapes ×1000, area over labels already
302
+ * placed ×100, other routes it covers ×10, plus how far it moved. Labels
303
+ * that are not moving are placed already; moving ones go in id order, each
304
+ * an obstacle for the next, so the answer depends on nothing else.
305
+ */
306
+ declare function placeLabels(input: PlaceLabelsInput, opts?: PlaceLabelsOptions): LabelPlacements;
307
+
308
+ /**
309
+ * `force` — relaxation, for a graph with no direction to read it in.
310
+ *
311
+ * Seeded from where the nodes already are rather than from a fresh random
312
+ * scatter: an author who has arranged half a diagram gets that arrangement
313
+ * relaxed, not replaced. There is no RNG in it at all — two coincident nodes
314
+ * are separated along a golden-angle spiral keyed to their index.
315
+ *
316
+ * The forces are the naive O(n²) kind — right for tens to hundreds of nodes,
317
+ * wrong for a 10k-node graph.
318
+ *
319
+ * **This is the one layout that is not idempotent.** A relaxation re-run from
320
+ * its own output keeps relaxing; `direction` means nothing to it. Pins and the
321
+ * `tolerance` option are what keep a second pass from scrambling a settled
322
+ * diagram.
323
+ */
324
+
325
+ /** Options for {@link force} and {@link forceRelaxation}. `direction` is
326
+ * ignored. */
327
+ interface ForceOptions extends LayoutOptions {
328
+ /** Ticks to run. Default 300 — one full cooling schedule. */
329
+ iterations?: number;
330
+ /** Rest length of an edge, in world units. Default 160. */
331
+ linkDistance?: number;
332
+ /** How hard an edge pulls, 0..1. Default 0.35. */
333
+ linkStrength?: number;
334
+ /** Pairwise repulsion. Negative repels; default -3000. */
335
+ charge?: number;
336
+ /** How hard the graph is held around its own centroid. Default 0.02. */
337
+ gravity?: number;
338
+ /** Clear space kept between two boxes, in world units. Default 24. */
339
+ padding?: number;
340
+ /** How hard a group draws its members together, 0..1. Default 0.2. */
341
+ groupStrength?: number;
342
+ }
343
+ /** One node as the relaxation sees it: a simulation point centered on the
344
+ * node's box. */
345
+ interface ForceBody extends SimulationNode {
346
+ id: string;
347
+ /** Half the node's box, so separation can work on the shape the author sees
348
+ * rather than on a point. */
349
+ hw: number;
350
+ hh: number;
351
+ }
352
+ /**
353
+ * The bodies and the force list, wound up and ready to tick.
354
+ */
355
+ interface ForceRelaxation {
356
+ bodies: ForceBody[];
357
+ byId: Map<string, ForceBody>;
358
+ /** Ids the caller must not move: declared pins, held as `fx`/`fy`. */
359
+ pinned: ReadonlySet<string>;
360
+ sim: ReturnType<typeof createSimulation<ForceBody>>;
361
+ /** Where a body's node's top-left sits, given the body's center. */
362
+ placed(): Map<string, {
363
+ x: number;
364
+ y: number;
365
+ }>;
366
+ }
367
+ /** Wind up a relaxation of `graph` without ticking it: bodies seeded from
368
+ * where the nodes are, pins held fixed. Tick `sim` yourself and read the
369
+ * top-lefts from `placed()`. */
370
+ declare function forceRelaxation(graph: Graph, opts?: ForceOptions): ForceRelaxation;
371
+ /** The force-directed layout. Unlike the others it is not idempotent: a
372
+ * re-run keeps relaxing, so pins and `tolerance` are what hold a settled
373
+ * diagram still. */
374
+ declare const force: (graph: Graph, opts?: ForceOptions) => LayoutResult;
375
+
376
+ /** The chain layout: the nodes `opts.chain` names (or the longest path) on
377
+ * one line in order, and every other node hung beside the chain node it is
378
+ * fewest edges from, on the `opts.chainSide` side. Its `plan` also routes
379
+ * each edge between a chain node and a node hung beside it through a track
380
+ * of its own, so a fan of them into one node does not cross itself. */
381
+ declare const chain: LayoutFn;
382
+
383
+ /** Which edges a layered layout turns around so that no cycle is left. */
384
+
385
+ /** Edge ids that close a cycle, found by a depth-first walk in graph order.
386
+ * Which edges get named depends only on the node order, so it is the same
387
+ * every run. A self-loop is not one: it points neither way. */
388
+ declare function backEdges(graph: Graph): Set<string>;
389
+ /** Longest-path rank per node: one past its deepest predecessor, ignoring
390
+ * `back`. Every node with no predecessor is rank 0. */
391
+ declare function ranksOf(graph: Graph, back: ReadonlySet<string>): Map<string, number>;
392
+
393
+ /**
394
+ * The layered layout's whole answer: where each node goes, and where each long
395
+ * edge runs, read off where its placeholders were placed.
396
+ */
397
+
398
+ /** What {@link layeredPlan} works out. */
399
+ interface LayeredPlan {
400
+ /** The new top-left of every node that moves, as `layered` returns it. */
401
+ moves: LayoutResult;
402
+ /** Each node's rank. */
403
+ ranks: ReadonlyMap<string, number>;
404
+ /** Ids of the graph edges laid out pointing the other way. */
405
+ back: ReadonlySet<string>;
406
+ /** For each edge spanning ranks, keyed by the edge's id as drawn: points
407
+ * from its `from` end to its `to` end, two per rank it crosses, where it
408
+ * enters and leaves that rank. */
409
+ waypoints: ReadonlyMap<string, readonly Vec2[]>;
410
+ /** For each labeled edge given room, keyed by the edge's id as drawn: the
411
+ * point on its route that room is beside, as a `DiagramLabel`'s
412
+ * `{ near }` reads it. */
413
+ labels: ReadonlyMap<string, Vec2>;
414
+ /** For each edge running between ranks, keyed by the edge's id as drawn:
415
+ * the compass ports facing the way it runs (see `layeredPorts`). */
416
+ ports: ReadonlyMap<string, LayoutPorts>;
417
+ /** Pinned nodes after the first that the layout could not be built
418
+ * around: covering one with its rank's band would bring that band nearer
419
+ * another pinned rank's than the bands between them allow with every gap
420
+ * at a quarter of `rankGap` beyond its groups' insets, or would put a
421
+ * group's box over a pin it does not hold. They stay put, and the rest is
422
+ * laid out around the slot each would have had. */
423
+ unheld: ReadonlySet<string>;
424
+ }
425
+ /** The layered layout, with the ranks and edge routes it settled on. */
426
+ declare function layeredPlan(graph: Graph, opts?: LayoutOptions): LayeredPlan;
427
+
428
+ /** Ranking by network simplex (Gansner, Koutsofios, North and Vo, 1993: "A
429
+ * Technique for Drawing Directed Graphs", the ranking Graphviz dot uses). */
430
+
431
+ /**
432
+ * Each node's rank, chosen to make the total edge length as short as it can:
433
+ * the sum over edges of `rank(to) - rank(from)`, with every edge at least one
434
+ * rank long and every edge weighing the same.
435
+ *
436
+ * An edge in `back` counts as running the other way, so it is ranked and kept
437
+ * forward like any other; a self-loop is ignored; parallel edges each count.
438
+ * Each connected component is ranked on its own and starts at rank 0.
439
+ *
440
+ * Starts from the longest-path ranking and improves it by network simplex,
441
+ * stopping after a number of rounds proportional to the edge count. Every
442
+ * round leaves a valid ranking, so the answer is valid even when the cap is
443
+ * hit. Ties break by graph order — node index, then edge index — so the same
444
+ * graph always ranks the same.
445
+ *
446
+ * If `back` leaves a cycle, no valid ranking exists; that component keeps its
447
+ * longest-path start, which `ranksOf` would also give.
448
+ */
449
+ declare function ranksByLength(graph: Graph, back: ReadonlySet<string>): Map<string, number>;
450
+
451
+ /**
452
+ * `layered` — ranks running one way, nodes running across.
453
+ *
454
+ * The flowchart / pipeline layout: an edge points from one rank to the next.
455
+ * The steps live in `./layered/`: edges closing a cycle are turned around
456
+ * (`cycles`), ranks are chosen to keep edges short (`rank`), an edge spanning
457
+ * ranks gets a placeholder in each rank it crosses (`expand`), each rank is
458
+ * ordered (`order`), and nodes are lined up with their neighbors (`place`).
459
+ * `plan` puts it together.
460
+ *
461
+ * Under `order: 'seeded'` (the default) each rank keeps the order its nodes
462
+ * and edges already sit in, so a re-run moves nothing. `order: 'barycenter'`
463
+ * reorders to cut crossings. Either way, a placeholder's position routes its
464
+ * long edge, which `layered.plan` hands back.
465
+ */
466
+
467
+ /** The ranked layout: each edge points one rank further along `direction`.
468
+ * Under `order: 'seeded'` a re-run moves nothing; `order: 'barycenter'`
469
+ * reorders ranks to cut crossings. */
470
+ declare const layered: LayoutFn;
471
+
472
+ /** A graph read as trees: its roots, each node's children in order, and each node's depth. */
473
+ interface Forest {
474
+ roots: GraphNode[];
475
+ children: Map<string, GraphNode[]>;
476
+ depth: Map<string, number>;
477
+ }
478
+ /** The forest a depth-first walk in graph order finds. Exported because "which
479
+ * node ended up whose child" is the question anyone debugging a tree layout
480
+ * asks first. */
481
+ declare function forestOf(graph: Graph, crossAxis: 'x' | 'y'): Forest;
482
+ /** The hierarchy layout: each parent centered over its children's block, one
483
+ * rank per depth. A graph that is not a tree lays out as the forest
484
+ * {@link forestOf} reads from it. */
485
+ declare const tree: LayoutFn;
486
+
487
+ /** The outline as a closed polyline: `[x0, y0, x1, y1, …]`. */
488
+ declare function outlinePolyline(outline: Outline, bounds: Bounds): number[];
489
+ /**
490
+ * Where the ray from `center` through `point` last crosses `poly`, or `null`
491
+ * when it never does.
492
+ *
493
+ * The *last* crossing rather than the first: a concave outline can be crossed
494
+ * more than once, and the far side is the boundary an edge should attach to.
495
+ */
496
+ declare function rayHit(poly: readonly number[], center: {
497
+ x: number;
498
+ y: number;
499
+ }, point: {
500
+ x: number;
501
+ y: number;
502
+ }): {
503
+ x: number;
504
+ y: number;
505
+ } | null;
506
+ /**
507
+ * Every port moved onto `outline`. A port the ray misses — one at the node's
508
+ * dead center, with no direction to cast along — is left where it was.
509
+ */
510
+ declare function portsOnOutline(ports: readonly Port[], outline: Outline, bounds: Bounds): Port[];
511
+
512
+ export { type AvoidInput as A, labelCenter as B, COMPASS as C, DEFAULT_PORTS as D, layered as E, type ForceOptions as F, layeredPlan as G, outlinePolyline as H, placeLabels as I, portOf as J, portSitesOf as K, type LabelPlacements as L, portsOf as M, portsOnOutline as N, ranksByLength as O, type PortsOptions as P, ranksOf as Q, rayHit as R, routeHolds as S, tree as T, type AvoidRouter as a, type AvoidOptions as b, type AvoidRoutes as c, type PlaceLabelsOptions as d, AVOID_DEFAULTS as e, type AvoidEdge as f, type AvoidEnd as g, type AvoidLabel as h, type AvoidObstacle as i, type AvoidPort as j, type AvoidUpdateOptions as k, type ForceBody as l, type ForceRelaxation as m, type Forest as n, type LayeredPlan as o, type PlaceLabel as p, type PlaceLabelsInput as q, type PlaceRoute as r, backEdges as s, chain as t, createAvoidRouter as u, edgeHops as v, footprintNode as w, force as x, forceRelaxation as y, forestOf as z };
package/dist/layout.js CHANGED
@@ -1,3 +1,5 @@
1
- export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, axesFor, backEdges, bodyOutline, boxForContent, buildBody, canvasMeasure, contentBox, extent, force, forceRelaxation, forestOf, graphOrder, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, portOf, portsOf, portsOnOutline, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree, untilSettled } from './chunk-JZ4CVLAB.js';
1
+ import './chunk-WDIE35AK.js';
2
+ export { AVOID_DEFAULTS, DEFAULT_HOP_SIZE, HOPS_DOC_KEY, createAvoidRouter, edgeHops, placeLabels } from './chunk-T6DQT553.js';
3
+ export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, PORT_LABEL_GAP, anchorOffset, axesFor, backEdges, bodyOutline, boxForContent, buildBody, canvasMeasure, chain, contentBox, extent, footprintNode, force, forceRelaxation, forestOf, graphOrder, labelCenter, layered, layeredPlan, layoutBody, layoutRowPorts, marginOf, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, planOf, plannerPlan, portLabelBox, portOf, portSitesOf, portsOf, portsOnOutline, ranksByLength, ranksOf, rayHit, routeHolds, seededOrder, settle, sizeToBody, translated, tree, untilSettled } from './chunk-IM6DHWZO.js';
2
4
  //# sourceMappingURL=layout.js.map
3
5
  //# sourceMappingURL=layout.js.map