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