@weasel-js/diagram 1.10.1 → 1.11.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/README.md +467 -25
- package/dist/{layout.entry-BUkiRKuX.d.ts → body-2lC7IiIA.d.ts} +245 -264
- package/dist/chunk-C4NAI6NV.js +2041 -0
- package/dist/chunk-C4NAI6NV.js.map +1 -0
- package/dist/chunk-NUMMW5BI.js +2865 -0
- package/dist/chunk-NUMMW5BI.js.map +1 -0
- package/dist/effect.d.ts +417 -0
- package/dist/effect.js +1283 -0
- package/dist/effect.js.map +1 -0
- package/dist/fromData-GKqRgPFs.d.ts +379 -0
- package/dist/index.d.ts +181 -327
- package/dist/index.js +2 -1434
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +2 -1
- package/dist/layout.entry-BoWFcR4G.d.ts +285 -0
- package/dist/layout.js +1 -1
- package/package.json +10 -6
- package/dist/chunk-JZ4CVLAB.js +0 -1247
- package/dist/chunk-JZ4CVLAB.js.map +0 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { A as GraphNode, b as PortSpec, h as DiagramNodeReader, g as DiagramNodeLike, f as Port, d as LayoutOptions, G as Graph, i as LayoutResult, c as LayoutFn, L as LayoutPorts, O as Outline, q as Bounds } from './body-2lC7IiIA.js';
|
|
2
|
+
import { SimulationNode, createSimulation } from '@weasel-js/simulation';
|
|
3
|
+
import { PoseDescriptor, Vec2 } 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
|
+
* `force` — relaxation, for a graph with no direction to read it in.
|
|
83
|
+
*
|
|
84
|
+
* Seeded from where the nodes already are rather than from a fresh random
|
|
85
|
+
* scatter: an author who has arranged half a diagram gets that arrangement
|
|
86
|
+
* relaxed, not replaced. There is no RNG in it at all — two coincident nodes
|
|
87
|
+
* are separated along a golden-angle spiral keyed to their index.
|
|
88
|
+
*
|
|
89
|
+
* The forces are the naive O(n²) kind — right for tens to hundreds of nodes,
|
|
90
|
+
* wrong for a 10k-node graph.
|
|
91
|
+
*
|
|
92
|
+
* **This is the one layout that is not idempotent.** A relaxation re-run from
|
|
93
|
+
* its own output keeps relaxing; `direction` means nothing to it. Pins and the
|
|
94
|
+
* `tolerance` option are what keep a second pass from scrambling a settled
|
|
95
|
+
* diagram.
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/** Options for {@link force} and {@link forceRelaxation}. `direction` is
|
|
99
|
+
* ignored. */
|
|
100
|
+
interface ForceOptions extends LayoutOptions {
|
|
101
|
+
/** Ticks to run. Default 300 — one full cooling schedule. */
|
|
102
|
+
iterations?: number;
|
|
103
|
+
/** Rest length of an edge, in world units. Default 160. */
|
|
104
|
+
linkDistance?: number;
|
|
105
|
+
/** How hard an edge pulls, 0..1. Default 0.35. */
|
|
106
|
+
linkStrength?: number;
|
|
107
|
+
/** Pairwise repulsion. Negative repels; default -3000. */
|
|
108
|
+
charge?: number;
|
|
109
|
+
/** How hard the graph is held around its own centroid. Default 0.02. */
|
|
110
|
+
gravity?: number;
|
|
111
|
+
/** Clear space kept between two boxes, in world units. Default 24. */
|
|
112
|
+
padding?: number;
|
|
113
|
+
/** How hard a group draws its members together, 0..1. Default 0.2. */
|
|
114
|
+
groupStrength?: number;
|
|
115
|
+
}
|
|
116
|
+
/** One node as the relaxation sees it: a simulation point centered on the
|
|
117
|
+
* node's box. */
|
|
118
|
+
interface ForceBody extends SimulationNode {
|
|
119
|
+
id: string;
|
|
120
|
+
/** Half the node's box, so separation can work on the shape the author sees
|
|
121
|
+
* rather than on a point. */
|
|
122
|
+
hw: number;
|
|
123
|
+
hh: number;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The bodies and the force list, wound up and ready to tick.
|
|
127
|
+
*/
|
|
128
|
+
interface ForceRelaxation {
|
|
129
|
+
bodies: ForceBody[];
|
|
130
|
+
byId: Map<string, ForceBody>;
|
|
131
|
+
/** Ids the caller must not move: declared pins, held as `fx`/`fy`. */
|
|
132
|
+
pinned: ReadonlySet<string>;
|
|
133
|
+
sim: ReturnType<typeof createSimulation<ForceBody>>;
|
|
134
|
+
/** Where a body's node's top-left sits, given the body's center. */
|
|
135
|
+
placed(): Map<string, {
|
|
136
|
+
x: number;
|
|
137
|
+
y: number;
|
|
138
|
+
}>;
|
|
139
|
+
}
|
|
140
|
+
/** Wind up a relaxation of `graph` without ticking it: bodies seeded from
|
|
141
|
+
* where the nodes are, pins held fixed. Tick `sim` yourself and read the
|
|
142
|
+
* top-lefts from `placed()`. */
|
|
143
|
+
declare function forceRelaxation(graph: Graph, opts?: ForceOptions): ForceRelaxation;
|
|
144
|
+
/** The force-directed layout. Unlike the others it is not idempotent: a
|
|
145
|
+
* re-run keeps relaxing, so pins and `tolerance` are what hold a settled
|
|
146
|
+
* diagram still. */
|
|
147
|
+
declare const force: (graph: Graph, opts?: ForceOptions) => LayoutResult;
|
|
148
|
+
|
|
149
|
+
/** The chain layout: the nodes `opts.chain` names (or the longest path) on
|
|
150
|
+
* one line in order, and every other node hung beside the chain node it is
|
|
151
|
+
* fewest edges from, on the `opts.chainSide` side. Its `plan` also routes
|
|
152
|
+
* each edge between a chain node and a node hung beside it through a track
|
|
153
|
+
* of its own, so a fan of them into one node does not cross itself. */
|
|
154
|
+
declare const chain: LayoutFn;
|
|
155
|
+
|
|
156
|
+
/** Which edges a layered layout turns around so that no cycle is left. */
|
|
157
|
+
|
|
158
|
+
/** Edge ids that close a cycle, found by a depth-first walk in graph order.
|
|
159
|
+
* Which edges get named depends only on the node order, so it is the same
|
|
160
|
+
* every run. A self-loop is not one: it points neither way. */
|
|
161
|
+
declare function backEdges(graph: Graph): Set<string>;
|
|
162
|
+
/** Longest-path rank per node: one past its deepest predecessor, ignoring
|
|
163
|
+
* `back`. Every node with no predecessor is rank 0. */
|
|
164
|
+
declare function ranksOf(graph: Graph, back: ReadonlySet<string>): Map<string, number>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The layered layout's whole answer: where each node goes, and where each long
|
|
168
|
+
* edge runs, read off where its placeholders were placed.
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
/** What {@link layeredPlan} works out. */
|
|
172
|
+
interface LayeredPlan {
|
|
173
|
+
/** The new top-left of every node that moves, as `layered` returns it. */
|
|
174
|
+
moves: LayoutResult;
|
|
175
|
+
/** Each node's rank. */
|
|
176
|
+
ranks: ReadonlyMap<string, number>;
|
|
177
|
+
/** Ids of the graph edges laid out pointing the other way. */
|
|
178
|
+
back: ReadonlySet<string>;
|
|
179
|
+
/** For each edge spanning ranks, keyed by the edge's id as drawn: points
|
|
180
|
+
* from its `from` end to its `to` end, two per rank it crosses, where it
|
|
181
|
+
* enters and leaves that rank. */
|
|
182
|
+
waypoints: ReadonlyMap<string, readonly Vec2[]>;
|
|
183
|
+
/** For each labeled edge given room, keyed by the edge's id as drawn: the
|
|
184
|
+
* point on its route that room is beside, as a `DiagramLabel`'s
|
|
185
|
+
* `{ near }` reads it. */
|
|
186
|
+
labels: ReadonlyMap<string, Vec2>;
|
|
187
|
+
/** For each edge running between ranks, keyed by the edge's id as drawn:
|
|
188
|
+
* the compass ports facing the way it runs (see `layeredPorts`). */
|
|
189
|
+
ports: ReadonlyMap<string, LayoutPorts>;
|
|
190
|
+
/** Pinned nodes after the first that the layout could not be built
|
|
191
|
+
* around: covering one with its rank's band would bring that band nearer
|
|
192
|
+
* another pinned rank's than the bands between them allow with every gap
|
|
193
|
+
* at a quarter of `rankGap` beyond its groups' insets, or would put a
|
|
194
|
+
* group's box over a pin it does not hold. They stay put, and the rest is
|
|
195
|
+
* laid out around the slot each would have had. */
|
|
196
|
+
unheld: ReadonlySet<string>;
|
|
197
|
+
}
|
|
198
|
+
/** The layered layout, with the ranks and edge routes it settled on. */
|
|
199
|
+
declare function layeredPlan(graph: Graph, opts?: LayoutOptions): LayeredPlan;
|
|
200
|
+
|
|
201
|
+
/** Ranking by network simplex (Gansner, Koutsofios, North and Vo, 1993: "A
|
|
202
|
+
* Technique for Drawing Directed Graphs", the ranking Graphviz dot uses). */
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Each node's rank, chosen to make the total edge length as short as it can:
|
|
206
|
+
* the sum over edges of `rank(to) - rank(from)`, with every edge at least one
|
|
207
|
+
* rank long and every edge weighing the same.
|
|
208
|
+
*
|
|
209
|
+
* An edge in `back` counts as running the other way, so it is ranked and kept
|
|
210
|
+
* forward like any other; a self-loop is ignored; parallel edges each count.
|
|
211
|
+
* Each connected component is ranked on its own and starts at rank 0.
|
|
212
|
+
*
|
|
213
|
+
* Starts from the longest-path ranking and improves it by network simplex,
|
|
214
|
+
* stopping after a number of rounds proportional to the edge count. Every
|
|
215
|
+
* round leaves a valid ranking, so the answer is valid even when the cap is
|
|
216
|
+
* hit. Ties break by graph order — node index, then edge index — so the same
|
|
217
|
+
* graph always ranks the same.
|
|
218
|
+
*
|
|
219
|
+
* If `back` leaves a cycle, no valid ranking exists; that component keeps its
|
|
220
|
+
* longest-path start, which `ranksOf` would also give.
|
|
221
|
+
*/
|
|
222
|
+
declare function ranksByLength(graph: Graph, back: ReadonlySet<string>): Map<string, number>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* `layered` — ranks running one way, nodes running across.
|
|
226
|
+
*
|
|
227
|
+
* The flowchart / pipeline layout: an edge points from one rank to the next.
|
|
228
|
+
* The steps live in `./layered/`: edges closing a cycle are turned around
|
|
229
|
+
* (`cycles`), ranks are chosen to keep edges short (`rank`), an edge spanning
|
|
230
|
+
* ranks gets a placeholder in each rank it crosses (`expand`), each rank is
|
|
231
|
+
* ordered (`order`), and nodes are lined up with their neighbors (`place`).
|
|
232
|
+
* `plan` puts it together.
|
|
233
|
+
*
|
|
234
|
+
* Under `order: 'seeded'` (the default) each rank keeps the order its nodes
|
|
235
|
+
* and edges already sit in, so a re-run moves nothing. `order: 'barycenter'`
|
|
236
|
+
* reorders to cut crossings. Either way, a placeholder's position routes its
|
|
237
|
+
* long edge, which `layered.plan` hands back.
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
/** The ranked layout: each edge points one rank further along `direction`.
|
|
241
|
+
* Under `order: 'seeded'` a re-run moves nothing; `order: 'barycenter'`
|
|
242
|
+
* reorders ranks to cut crossings. */
|
|
243
|
+
declare const layered: LayoutFn;
|
|
244
|
+
|
|
245
|
+
/** A graph read as trees: its roots, each node's children in order, and each node's depth. */
|
|
246
|
+
interface Forest {
|
|
247
|
+
roots: GraphNode[];
|
|
248
|
+
children: Map<string, GraphNode[]>;
|
|
249
|
+
depth: Map<string, number>;
|
|
250
|
+
}
|
|
251
|
+
/** The forest a depth-first walk in graph order finds. Exported because "which
|
|
252
|
+
* node ended up whose child" is the question anyone debugging a tree layout
|
|
253
|
+
* asks first. */
|
|
254
|
+
declare function forestOf(graph: Graph, crossAxis: 'x' | 'y'): Forest;
|
|
255
|
+
/** The hierarchy layout: each parent centered over its children's block, one
|
|
256
|
+
* rank per depth. A graph that is not a tree lays out as the forest
|
|
257
|
+
* {@link forestOf} reads from it. */
|
|
258
|
+
declare const tree: LayoutFn;
|
|
259
|
+
|
|
260
|
+
/** The outline as a closed polyline: `[x0, y0, x1, y1, …]`. */
|
|
261
|
+
declare function outlinePolyline(outline: Outline, bounds: Bounds): number[];
|
|
262
|
+
/**
|
|
263
|
+
* Where the ray from `center` through `point` last crosses `poly`, or `null`
|
|
264
|
+
* when it never does.
|
|
265
|
+
*
|
|
266
|
+
* The *last* crossing rather than the first: a concave outline can be crossed
|
|
267
|
+
* more than once, and the far side is the boundary an edge should attach to.
|
|
268
|
+
*/
|
|
269
|
+
declare function rayHit(poly: readonly number[], center: {
|
|
270
|
+
x: number;
|
|
271
|
+
y: number;
|
|
272
|
+
}, point: {
|
|
273
|
+
x: number;
|
|
274
|
+
y: number;
|
|
275
|
+
}): {
|
|
276
|
+
x: number;
|
|
277
|
+
y: number;
|
|
278
|
+
} | null;
|
|
279
|
+
/**
|
|
280
|
+
* Every port moved onto `outline`. A port the ray misses — one at the node's
|
|
281
|
+
* dead center, with no direction to cast along — is left where it was.
|
|
282
|
+
*/
|
|
283
|
+
declare function portsOnOutline(ports: readonly Port[], outline: Outline, bounds: Bounds): Port[];
|
|
284
|
+
|
|
285
|
+
export { COMPASS as C, DEFAULT_PORTS as D, type ForceOptions as F, type LayeredPlan as L, type PortsOptions as P, type ForceBody as a, type ForceRelaxation as b, type Forest as c, backEdges as d, chain as e, footprintNode as f, force as g, forceRelaxation as h, forestOf as i, layeredPlan as j, portSitesOf as k, layered as l, portsOf as m, portsOnOutline as n, outlinePolyline as o, portOf as p, ranksOf as q, ranksByLength as r, rayHit as s, tree as t };
|
package/dist/layout.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
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-
|
|
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';
|
|
2
2
|
//# sourceMappingURL=layout.js.map
|
|
3
3
|
//# sourceMappingURL=layout.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/diagram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Node-link diagramming for weasel: any scene node becomes a diagram participant, with ports on its perimeter and an optional body builder for flowchart-shaped boxes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,15 +17,19 @@
|
|
|
17
17
|
"import": "./dist/layout.js",
|
|
18
18
|
"types": "./dist/layout.d.ts"
|
|
19
19
|
},
|
|
20
|
+
"./effect": {
|
|
21
|
+
"import": "./dist/effect.js",
|
|
22
|
+
"types": "./dist/effect.d.ts"
|
|
23
|
+
},
|
|
20
24
|
"./package.json": "./package.json"
|
|
21
25
|
},
|
|
22
26
|
"peerDependencies": {
|
|
23
27
|
"react": ">=18",
|
|
24
|
-
"@weasel-js/animator": "1.
|
|
25
|
-
"@weasel-js/core": "1.
|
|
26
|
-
"@weasel-js/font": "1.
|
|
27
|
-
"@weasel-js/geom": "1.
|
|
28
|
-
"@weasel-js/simulation": "1.
|
|
28
|
+
"@weasel-js/animator": "1.11.0",
|
|
29
|
+
"@weasel-js/core": "1.11.0",
|
|
30
|
+
"@weasel-js/font": "1.11.0",
|
|
31
|
+
"@weasel-js/geom": "1.11.0",
|
|
32
|
+
"@weasel-js/simulation": "1.11.0"
|
|
29
33
|
},
|
|
30
34
|
"author": "orochi235",
|
|
31
35
|
"homepage": "https://orochi235.github.io/weasel/",
|