@knowvah/dot-engine 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/async/collect.d.ts.map +1 -1
  2. package/dist/async/render-async.d.ts.map +1 -1
  3. package/dist/gvc/device-labels.d.ts +30 -0
  4. package/dist/gvc/device-labels.d.ts.map +1 -0
  5. package/dist/gvc/device-transform.d.ts +22 -0
  6. package/dist/gvc/device-transform.d.ts.map +1 -0
  7. package/dist/gvc/device.d.ts +2 -42
  8. package/dist/gvc/device.d.ts.map +1 -1
  9. package/dist/index.js +174 -137
  10. package/dist/index.js.map +4 -4
  11. package/dist/layout/dot/graph-label.d.ts +10 -0
  12. package/dist/layout/dot/graph-label.d.ts.map +1 -1
  13. package/dist/layout/fdp/normalize.d.ts +2 -1
  14. package/dist/layout/fdp/normalize.d.ts.map +1 -1
  15. package/dist/layout/fdp/xlayout.d.ts +1 -1
  16. package/dist/layout/fdp/xlayout.d.ts.map +1 -1
  17. package/dist/layout/neato/dijkstra.d.ts +0 -32
  18. package/dist/layout/neato/dijkstra.d.ts.map +1 -1
  19. package/dist/layout/neato/fdp-adjust.d.ts +14 -3
  20. package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
  21. package/dist/layout/neato/index.d.ts +5 -5
  22. package/dist/layout/neato/index.d.ts.map +1 -1
  23. package/dist/layout/neato/sc-adjust.d.ts +2 -9
  24. package/dist/layout/neato/sc-adjust.d.ts.map +1 -1
  25. package/dist/layout/neato/simple-scale.d.ts +15 -0
  26. package/dist/layout/neato/simple-scale.d.ts.map +1 -0
  27. package/dist/layout/sfdp/index.d.ts.map +1 -1
  28. package/dist/render/public.d.ts +5 -0
  29. package/dist/render/public.d.ts.map +1 -1
  30. package/dist/render.js +171 -134
  31. package/dist/render.js.map +4 -4
  32. package/package.json +1 -1
  33. package/src/async/collect.ts +2 -14
  34. package/src/async/render-async.ts +1 -9
  35. package/src/gvc/device-labels.ts +98 -0
  36. package/src/gvc/device-transform.ts +58 -0
  37. package/src/gvc/device.ts +8 -132
  38. package/src/layout/dot/graph-label.ts +6 -1
  39. package/src/layout/fdp/normalize.ts +12 -8
  40. package/src/layout/fdp/xlayout.ts +8 -6
  41. package/src/layout/neato/dijkstra.ts +0 -141
  42. package/src/layout/neato/fdp-adjust.ts +33 -8
  43. package/src/layout/neato/index.ts +13 -12
  44. package/src/layout/neato/sc-adjust.ts +2 -16
  45. package/src/layout/neato/simple-scale.ts +54 -0
  46. package/src/layout/sfdp/index.ts +2 -1
  47. package/src/render/public.ts +6 -5
  48. package/dist/layout/neato/overlap.d.ts +0 -85
  49. package/dist/layout/neato/overlap.d.ts.map +0 -1
  50. package/src/layout/neato/overlap.ts +0 -158
@@ -1,8 +1,6 @@
1
1
  // SPDX-License-Identifier: EPL-2.0
2
2
  /** @see lib/neatogen/dijkstra.c */
3
3
 
4
- import type { GraphSgd } from './sgd.js';
5
-
6
4
  /**
7
5
  * Sparse-graph vertex descriptor used by neato layout algorithms.
8
6
  * Matches `vtx_data` in lib/neatogen/sparsegraph.h.
@@ -31,9 +29,6 @@ export interface TermSgd {
31
29
  w: number; // weight = 1/d²
32
30
  }
33
31
 
34
- /** Sentinel used for unvisited BFS nodes (matches C INT_MAX). */
35
- const BFS_INF = 0x7fffffff;
36
-
37
32
  // ---------------------------------------------------------------------------
38
33
  // Min-heap (keyed by Float32Array distances)
39
34
  // ---------------------------------------------------------------------------
@@ -158,139 +153,3 @@ export function dijkstra(
158
153
  dijkstraRelax(closest, closestDist, graph, dist, heap);
159
154
  }
160
155
  }
161
-
162
- // ---------------------------------------------------------------------------
163
- // Public: All-pairs shortest paths (packed upper-triangular)
164
- // ---------------------------------------------------------------------------
165
-
166
- /**
167
- * BFS single-source distances for unweighted graphs.
168
- * Fills `dist` with hop counts; unreachable nodes get BFS_INF (0x7fffffff).
169
- * edges[0] of each vertex is the self-loop and is skipped.
170
- * @see lib/neatogen/bfs.c:bfs
171
- */
172
- export function bfsDistances(
173
- src: number,
174
- graph: VtxData[],
175
- n: number,
176
- dist: Float32Array,
177
- ): void {
178
- for (let i = 0; i < n; i++) dist[i] = BFS_INF;
179
- dist[src] = 0;
180
- const queue = new Int32Array(n);
181
- let head = 0, tail = 0;
182
- queue[tail++] = src;
183
- while (head < tail) {
184
- const u = queue[head++];
185
- const d = dist[u] + 1;
186
- const vtx = graph[u];
187
- for (let e = 1; e < vtx.nedges; e++) {
188
- const v = vtx.edges[e];
189
- if (dist[v] === BFS_INF) { dist[v] = d; queue[tail++] = v; }
190
- }
191
- }
192
- }
193
-
194
- /**
195
- * All-pairs shortest paths (unweighted), packed upper-triangular.
196
- * Length n*(n+1)/2; index for (i,j) j>=i: i*(2n-i-1)/2 + j.
197
- * @see lib/neatogen/stress.c:compute_apsp_packed
198
- */
199
- export function computeApspPacked(graph: VtxData[], n: number): Float32Array {
200
- const result = new Float32Array(n * (n + 1) / 2);
201
- const di = new Float32Array(n);
202
- let count = 0;
203
- for (let i = 0; i < n; i++) {
204
- bfsDistances(i, graph, n, di);
205
- for (let j = i; j < n; j++) result[count++] = di[j];
206
- }
207
- return result;
208
- }
209
-
210
- /**
211
- * All-pairs shortest paths (weighted), packed upper-triangular.
212
- * Length n*(n+1)/2.
213
- * @see lib/neatogen/stress.c:compute_weighted_apsp_packed
214
- */
215
- export function computeWeightedApspPacked(
216
- graph: VtxData[],
217
- n: number,
218
- ): Float32Array {
219
- const result = new Float32Array(n * (n + 1) / 2);
220
- const di = new Float32Array(n);
221
- let count = 0;
222
- for (let i = 0; i < n; i++) {
223
- dijkstra(i, graph, n, di);
224
- for (let j = i; j < n; j++) result[count++] = di[j];
225
- }
226
- return result;
227
- }
228
-
229
- // ---------------------------------------------------------------------------
230
- // dijkstraSgd helpers
231
- // ---------------------------------------------------------------------------
232
-
233
- /** Initialise SGD dists from source's adjacency list. @internal */
234
- export function sgdInitDist(
235
- graph: GraphSgd,
236
- src: number,
237
- dists: Float32Array,
238
- ): void {
239
- dists[src] = 0;
240
- const end = graph.sources[src + 1];
241
- for (let i = graph.sources[src]; i < end; i++) {
242
- dists[graph.targets[i]] = graph.weights[i];
243
- }
244
- }
245
-
246
- /** Relax SGD neighbours of `closest` and push updates onto `heap`. @internal */
247
- export function sgdRelaxNeighbors(
248
- graph: GraphSgd,
249
- closest: number,
250
- d: number,
251
- dists: Float32Array,
252
- heap: number[],
253
- ): void {
254
- const end = graph.sources[closest + 1];
255
- for (let i = graph.sources[closest]; i < end; i++) {
256
- const target = graph.targets[i];
257
- const nd = d + graph.weights[i];
258
- if (nd < dists[target]) {
259
- dists[target] = nd;
260
- heapPush(heap, dists, target);
261
- }
262
- }
263
- }
264
-
265
- // ---------------------------------------------------------------------------
266
- // Public: dijkstraSgd
267
- // ---------------------------------------------------------------------------
268
-
269
- /**
270
- * Single-source shortest paths for the SGD engine.
271
- * Writes terms for all reachable `j < src` and pinned nodes.
272
- * Returns the count of terms written.
273
- *
274
- * @see lib/neatogen/dijkstra.c:dijkstra_sgd
275
- */
276
- export function dijkstraSgd(
277
- graph: GraphSgd,
278
- src: number,
279
- terms: TermSgd[],
280
- ): number {
281
- const n = graph.n;
282
- const dists = new Float32Array(n).fill(Infinity);
283
- sgdInitDist(graph, src, dists);
284
- const heap = heapBuildExcluding(src, n, dists);
285
- let offset = 0;
286
- while (heap.length > 0) {
287
- const closest = heapPop(heap, dists);
288
- const d = dists[closest];
289
- if (!isFinite(d)) break;
290
- if (graph.pinneds[closest] || closest < src) {
291
- terms[offset++] = { i: src, j: closest, d, w: 1 / (d * d) };
292
- }
293
- sgdRelaxNeighbors(graph, closest, d, dists, heap);
294
- }
295
- return offset;
296
- }
@@ -18,6 +18,8 @@ import {
18
18
  } from '../sfdp/sparse-matrix.js';
19
19
  import { removeOverlapPrism } from './overlap-prism.js';
20
20
  import { scAdjust } from './sc-adjust.js';
21
+ import { simpleScale } from './simple-scale.js';
22
+ import { normalizeG } from '../fdp/normalize.js';
21
23
  import { AM_ORTHO, AM_ORTHO_YX, AM_ORTHOXY, AM_ORTHOYX, AM_PORTHO, AM_PORTHO_YX, AM_PORTHOXY, AM_PORTHOYX, cAdjust } from './constraint-adjust.js';
22
24
  import { countOverlapIn, sAdjustGraph } from './adjust-info.js';
23
25
  import { vpscAdjust } from './vpsc-adjust.js';
@@ -149,12 +151,26 @@ const ADJUST_MODES = new Map<string, readonly [number, string]>([
149
151
  ['porthoyx', [AM_PORTHOYX, 'yx pseudo-orthogonal constraints']],
150
152
  ]);
151
153
 
154
+ /**
155
+ * getAdjustMode's fallback warning: a value that is not a mode name, not
156
+ * prism*, and not a boolean is used as `false` (prism) with this warning.
157
+ * @see lib/neatogen/adjust.c:getAdjustMode (unmappable branch)
158
+ */
159
+ export function warnUnrecognizedOverlap(flag: string | undefined): void {
160
+ if (flag === undefined || flag === '') return;
161
+ const s = flag.toLowerCase();
162
+ if (s.startsWith('prism') || NAMED_MODES.has(s)) return;
163
+ if (mapBoolDflt(flag, false) === mapBoolDflt(flag, true)) return;
164
+ console.warn(`Unrecognized overlap value "${flag}" - using false`);
165
+ }
166
+
152
167
  /**
153
168
  * Resolve an `overlap` value to its adjust mode (the PRISM try count lands in
154
- * `value`).
169
+ * `value`), warning as C does for an unrecognized value.
155
170
  * @see lib/neatogen/adjust.c:getAdjustMode
156
171
  */
157
172
  export function getAdjustMode(flag: string | undefined): AdjustData {
173
+ warnUnrecognizedOverlap(flag);
158
174
  const ntry = overlapPrismTries(flag);
159
175
  if (ntry !== null) return { mode: AM_PRISM, print: 'prism', value: ntry };
160
176
  const named = ADJUST_MODES.get((flag ?? '').toLowerCase());
@@ -208,15 +224,16 @@ function adjustVoronoi(g: Graph, am: AdjustData): number {
208
224
 
209
225
  /**
210
226
  * Use `am` to decide if and how to remove node overlaps. normalize and
211
- * simpleScale (only active with the `normalize`/`scale` attributes) are not
212
- * ported (see sc-adjust module doc).
227
+ * simpleScale run first, whatever the mode (the `normalize` / `scale`
228
+ * attributes); returns non-zero if nodes moved.
213
229
  * @see lib/neatogen/adjust.c:removeOverlapWith
214
230
  */
215
231
  export function removeOverlapWith(g: Graph, am: AdjustData): number {
216
232
  if (g.nodes.size < 2) return 0;
217
- if (am.mode === AM_NONE) return 0;
218
- if (am.mode > AM_SCALE) return adjustLate(g, am);
219
- return am.mode === AM_SCALE ? sAdjustGraph(g) : adjustVoronoi(g, am);
233
+ const nret = normalizeG(g) + simpleScale(g);
234
+ if (am.mode === AM_NONE) return nret;
235
+ if (am.mode > AM_SCALE) return nret + adjustLate(g, am);
236
+ return nret + (am.mode === AM_SCALE ? sAdjustGraph(g) : adjustVoronoi(g, am));
220
237
  }
221
238
 
222
239
  /**
@@ -224,6 +241,14 @@ export function removeOverlapWith(g: Graph, am: AdjustData): number {
224
241
  * @see lib/neatogen/adjust.c:adjustNodes / removeOverlapAs
225
242
  */
226
243
  export function adjustNodesFull(g: Graph): number {
227
- const flag = g.attrs.get('overlap') ?? g.root.attrs.get('overlap');
228
- return removeOverlapWith(g, getAdjustMode(flag));
244
+ if (g.nodes.size < 2) return 0; // removeOverlapAs returns before getAdjustMode
245
+ return removeOverlapWith(g, graphAdjustMode(g));
246
+ }
247
+
248
+ /**
249
+ * The graph's `overlap` value resolved once (agget falls back to the root).
250
+ * @see lib/neatogen/adjust.c:graphAdjustMode
251
+ */
252
+ export function graphAdjustMode(g: Graph): AdjustData {
253
+ return getAdjustMode(g.attrs.get('overlap') ?? g.root.attrs.get('overlap'));
229
254
  }
@@ -25,7 +25,7 @@ import {
25
25
  MODEL_MDS,
26
26
  } from './init.js';
27
27
  import { neatoMode } from './start.js';
28
- import { adjustNodesFull } from './fdp-adjust.js';
28
+ import { removeOverlapWith, graphAdjustMode, type AdjustData } from './fdp-adjust.js';
29
29
  import { splineEdgesShifted, EDGETYPE_LINE } from './splines.js';
30
30
  import { setEdgeTypeFromAttr } from '../dot/index.js';
31
31
  import {
@@ -100,15 +100,14 @@ export function parseModel(g: Graph): number {
100
100
  // ---------------------------------------------------------------------------
101
101
 
102
102
  /**
103
- * Remove node overlaps per the `overlap` attribute: C's removeOverlapWith
104
- * dispatch (prism, scale family, oscale, ortho*, portho*, vpsc, voronoi) for
105
- * every mode, as neato_layout's adjustNodes call does.
103
+ * Remove node overlaps with the mode neato_layout resolved once
104
+ * (graphAdjustMode): C's removeOverlapWith dispatch for every mode.
106
105
  *
107
- * @see lib/neatogen/neatoinit.c:neato_layout (adjustNodes call)
106
+ * @see lib/neatogen/neatoinit.c:neato_layout (removeOverlapWith calls)
108
107
  * @see lib/neatogen/adjust.c:removeOverlapWith
109
108
  */
110
- export function maybeRemoveOverlap(g: Graph): void {
111
- adjustNodesFull(g);
109
+ export function maybeRemoveOverlap(g: Graph, am: AdjustData): void {
110
+ removeOverlapWith(g, am);
112
111
  }
113
112
 
114
113
  // ---------------------------------------------------------------------------
@@ -164,6 +163,8 @@ export function neatoLayout(g: Graph): void {
164
163
  }
165
164
 
166
165
  const mode = parseMode(g);
166
+ // C resolves the overlap mode once, before any component. @see neatoinit.c:1369
167
+ const am = graphAdjustMode(g);
167
168
  const model = parseModel(g);
168
169
 
169
170
  // C uses pccomps (pin-aware): components with a pinned node are collected
@@ -172,10 +173,10 @@ export function neatoLayout(g: Graph): void {
172
173
  ? pccomps(g, '_neato_cc')
173
174
  : { graphs: [g], pinned: false };
174
175
  if (comps.length > 1) {
175
- layoutComponents(g, comps, mode, model, pinned);
176
+ layoutComponents(g, comps, { mode, model, am }, pinned);
176
177
  } else {
177
178
  solveModel(g, mode, model); // srand48 happens inside (C checkStart)
178
- maybeRemoveOverlap(g);
179
+ maybeRemoveOverlap(g, am);
179
180
  // C: spline_edges shifts pos to the origin, syncs coord, routes.
180
181
  splineEdgesShifted(g);
181
182
  }
@@ -236,13 +237,13 @@ function addClusters(g: Graph): void {
236
237
  * @see lib/neatogen/neatoinit.c:neato_layout (Pack >= 0 branch)
237
238
  */
238
239
  function layoutComponents(
239
- g: Graph, comps: Graph[], mode: number, model: number, pinned: boolean,
240
+ g: Graph, comps: Graph[], solve: { mode: number; model: number; am: AdjustData }, pinned: boolean,
240
241
  ): void {
241
242
  for (const gc of comps) {
242
243
  // @see lib/neatogen/neatoinit.c:1398 (setEdgeType FUNCTION, per component)
243
244
  setEdgeTypeFromAttr(gc, EDGETYPE_LINE);
244
- solveModel(gc, mode, model);
245
- maybeRemoveOverlap(gc);
245
+ solveModel(gc, solve.mode, solve.model);
246
+ maybeRemoveOverlap(gc, solve.am);
246
247
  splineEdgesShifted(gc);
247
248
  }
248
249
  // C sets fixed[0]=true when a pinned node is present: pccomps merges every
@@ -11,9 +11,8 @@
11
11
  * Using Constrained Optimization", Constraints 8(2):143-172, 2003 (per the
12
12
  * C source comment).
13
13
  *
14
- * Other overlap modes (voronoi, prism, vpsc, ortho*) remain unported; with
15
- * the attr unset C's getAdjustMode resolves to AM_NONE and the pass is a
16
- * no-op (normalize/simpleScale only fire on rare attrs, also unported).
14
+ * Dispatched from removeOverlapWith (fdp-adjust.ts) for overlap=scale,
15
+ * scalexy and compress.
17
16
  *
18
17
  * @see lib/neatogen/constraint.c:scAdjust
19
18
  * @see lib/neatogen/adjust.c:removeOverlapWith / getAdjustMode
@@ -191,16 +190,3 @@ export function scAdjust(g: Graph, equal: number): number {
191
190
  return 1;
192
191
  }
193
192
 
194
- /**
195
- * The adjustNodes overlap-mode subset reaching scAdjust; other modes are
196
- * no-ops (see module doc).
197
- * @see lib/neatogen/adjust.c:999 adjustNodes, :776 adjustMode table
198
- */
199
- export function adjustNodesScale(g: Graph): number {
200
- // agget falls back to the root graph's attrs for component subgraphs.
201
- const flag = (g.attrs.get('overlap') ?? g.root.attrs.get('overlap') ?? '').toLowerCase();
202
- if (flag === 'scale') return scAdjust(g, 1); // AM_NSCALE
203
- if (flag === 'scalexy') return scAdjust(g, 0); // AM_SCALEXY
204
- if (flag === 'compress') return scAdjust(g, -1); // AM_COMPRESS
205
- return 0; // unset -> AM_NONE; other modes unported
206
- }
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: EPL-2.0
2
+
3
+ /**
4
+ * The `scale` graph attribute: multiply every node position by (sx, sy)
5
+ * before overlap removal. removeOverlapWith runs it right after normalize,
6
+ * whatever the overlap mode.
7
+ *
8
+ * @see lib/neatogen/adjust.c:simpleScale
9
+ */
10
+
11
+ import type { Graph } from '../../model/graph.js';
12
+ import { aggetGraph } from '../fdp/fdp-model.js';
13
+
14
+ /** @see lib/neatogen/adjust.c:ISZERO */
15
+ const ISZERO = (d: number): boolean => Math.abs(d) < 0.000000001;
16
+
17
+ /** One C `%lf` conversion: optional leading whitespace, then a strtod number. */
18
+ const LF = /^\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/;
19
+
20
+ /**
21
+ * C `sscanf(p, "%lf,%lf", &x, &y)`: the values converted, in order (0, 1 or
22
+ * 2 of them). An empty string returns none — C's EOF (-1) there reads an
23
+ * uninitialised pointf, undefined behaviour the port defines as a no-op.
24
+ */
25
+ function scanPair(p: string): number[] {
26
+ const m1 = LF.exec(p);
27
+ if (m1 === null) return [];
28
+ const rest = p.slice(m1[0].length);
29
+ if (!rest.startsWith(',')) return [parseFloat(m1[1]!)];
30
+ const m2 = LF.exec(rest.slice(1));
31
+ return m2 === null ? [parseFloat(m1[1]!)] : [parseFloat(m1[1]!), parseFloat(m2[1]!)];
32
+ }
33
+
34
+ /**
35
+ * Scale node positions by the `scale` attribute ("s" or "sx,sy"). Returns 1
36
+ * if nodes moved, else 0 (unset, unparsable, a zero factor, or 1,1).
37
+ * @see lib/neatogen/adjust.c:simpleScale
38
+ */
39
+ export function simpleScale(g: Graph): number {
40
+ const p = aggetGraph(g, 'scale');
41
+ if (p === undefined) return 0;
42
+ const sc = scanPair(p);
43
+ if (sc.length === 0) return 0;
44
+ const x = sc[0]!;
45
+ if (ISZERO(x)) return 0;
46
+ const y = sc.length === 1 ? x : sc[1]!;
47
+ if (ISZERO(y)) return 0;
48
+ if (y === 1 && x === 1) return 0;
49
+ for (const n of g.nodes.values()) {
50
+ n.info.pos![0] *= x;
51
+ n.info.pos![1] *= y;
52
+ }
53
+ return 1;
54
+ }
@@ -31,7 +31,7 @@ import { CL_OFFSET } from '../twopi/pipeline.js';
31
31
  import { placeGraphLabel } from '../dot/position-bbox.js';
32
32
  import { gvPostprocess } from '../../common/postproc.js';
33
33
  import { aggetGraph } from '../fdp/fdp-model.js';
34
- import { overlapPrismTries, adjustNodesFull } from '../neato/fdp-adjust.js';
34
+ import { overlapPrismTries, adjustNodesFull, warnUnrecognizedOverlap } from '../neato/fdp-adjust.js';
35
35
  import { lateDouble } from '../../common/nodeinit.js';
36
36
  import {
37
37
  type SpringElectricalControl,
@@ -98,6 +98,7 @@ function resolveControl(g: Graph): {
98
98
  // graphAdjustMode(g, &am, "prism0"): agget NULL (unset) → "prism0" default.
99
99
  const overlap = aggetGraph(g, 'overlap');
100
100
  const flag = overlap === undefined ? 'prism0' : overlap;
101
+ warnUnrecognizedOverlap(flag); // graphAdjustMode → getAdjustMode, once per layout
101
102
  const ntry = overlapPrismTries(flag);
102
103
  if (ntry !== null) {
103
104
  // AM_PRISM && doAdjust: overlap removal happens inside sfdp.
@@ -60,13 +60,14 @@ export interface RenderOptions {
60
60
  }
61
61
 
62
62
  // ---------------------------------------------------------------------------
63
- // Private helpers (mirrors index.ts)
63
+ // Argument validation (shared with the async entry points)
64
64
  // ---------------------------------------------------------------------------
65
65
 
66
-
67
-
68
- /** Reject a bad `g` / `format` / `opts` before any work starts. */
69
- function checkRenderArgs(g: unknown, format: unknown, opts: unknown): void {
66
+ /**
67
+ * Reject a bad `g` / `format` / `opts` before any work starts.
68
+ * @internal shared with src/async/render-async.ts; not part of the public API.
69
+ */
70
+ export function checkRenderArgs(g: unknown, format: unknown, opts: unknown): void {
70
71
  if (typeof g !== 'object' || g === null) {
71
72
  throw invalidArgType('g', 'object', g);
72
73
  }
@@ -1,85 +0,0 @@
1
- /**
2
- * VPSC-based overlap removal for the neato layout engine.
3
- *
4
- * Two independent VPSC passes (X then Y) ensure that no two node bounding
5
- * boxes intersect after the call returns. Each pass builds its own Variables,
6
- * Rectangles, and Constraints; none are shared between passes.
7
- *
8
- * @see lib/neatogen/adjust.c:vpscAdjust
9
- * @see lib/vpsc/csolve_VPSC.h
10
- */
11
- import type { Node } from '../../model/node.js';
12
- import { type Variable, type Constraint, VPSC, Rectangle } from '../../vpsc/index.js';
13
- /**
14
- * Builds one Rectangle per node centred on pos[], expanding each half-size
15
- * by sep/2 so the VPSC gap `(w_i+w_j)/2` incorporates the padding.
16
- *
17
- * @see lib/neatogen/adjust.c:vpscAdjust
18
- */
19
- export declare function buildRectangles(nodes: Node[], sep: {
20
- x: number;
21
- y: number;
22
- }): Rectangle[];
23
- /**
24
- * Allocates one Variable per node for a single VPSC axis pass.
25
- *
26
- * @see lib/vpsc/csolve_VPSC.h:newVariable
27
- */
28
- export declare function allocateVariables(nodes: Node[], axis: 0 | 1): Variable[];
29
- /**
30
- * Writes solved positions back into node.info.pos[axis].
31
- *
32
- * @see lib/neatogen/adjust.c:vpscAdjust (position write-back loop)
33
- */
34
- export declare function writePositions(nodes: Node[], vars: Variable[], axis: 0 | 1): void;
35
- /**
36
- * Tears down a VPSC pass in mandatory order (AD-4):
37
- * deleteVPSC → deleteConstraints → deleteVariable per variable.
38
- *
39
- * @see lib/neatogen/adjust.c:vpscAdjust teardown sequence
40
- */
41
- export declare function teardownPass(vpsc: VPSC, constraints: Constraint[], vars: Variable[]): void;
42
- /**
43
- * Runs one complete VPSC axis pass: solve constraints then write positions.
44
- *
45
- * @see lib/neatogen/adjust.c:vpscAdjust
46
- */
47
- export declare function runAxisPass(nodes: Node[], sep: {
48
- x: number;
49
- y: number;
50
- }, constraints: Constraint[], vars: Variable[], axis: 0 | 1): void;
51
- /**
52
- * One full VPSC pass on the X axis.
53
- *
54
- * @see lib/neatogen/adjust.c:vpscAdjust (X pass section)
55
- */
56
- export declare function runXPass(nodes: Node[], sep: {
57
- x: number;
58
- y: number;
59
- }): void;
60
- /**
61
- * One full VPSC pass on the Y axis.
62
- *
63
- * @see lib/neatogen/adjust.c:vpscAdjust (Y pass section)
64
- */
65
- export declare function runYPass(nodes: Node[], sep: {
66
- x: number;
67
- y: number;
68
- }): void;
69
- /**
70
- * Removes overlaps between nodes using two independent VPSC passes (X then Y).
71
- *
72
- * After this call no two node bounding boxes intersect (with sep padding).
73
- * Positions are written into node.info.pos[0] (X) and node.info.pos[1] (Y).
74
- *
75
- * @param nodes - Nodes with info.pos, info.width, info.height set.
76
- * @param sep - Minimum padding added beyond each node's half-size per axis.
77
- *
78
- * @see lib/neatogen/adjust.c:vpscAdjust
79
- * @see lib/vpsc/csolve_VPSC.h
80
- */
81
- export declare function removeOverlap(nodes: Node[], sep: {
82
- x: number;
83
- y: number;
84
- }): void;
85
- //# sourceMappingURL=overlap.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"overlap.d.ts","sourceRoot":"","sources":["../../../src/layout/neato/overlap.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,IAAI,EACJ,SAAS,EACV,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,SAAS,EAAE,CAQb;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,QAAQ,EAAE,CAExE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,EAAE,QAAQ,EAAE,EAChB,IAAI,EAAE,CAAC,GAAG,CAAC,GACV,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,UAAU,EAAE,EACzB,IAAI,EAAE,QAAQ,EAAE,GACf,IAAI,CAIN;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7B,WAAW,EAAE,UAAU,EAAE,EACzB,IAAI,EAAE,QAAQ,EAAE,EAChB,IAAI,EAAE,CAAC,GAAG,CAAC,GACV,IAAI,CAKN;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,IAAI,CAKN;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,IAAI,CAKN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,IAAI,CAIN"}
@@ -1,158 +0,0 @@
1
- // SPDX-License-Identifier: EPL-2.0
2
-
3
- /**
4
- * VPSC-based overlap removal for the neato layout engine.
5
- *
6
- * Two independent VPSC passes (X then Y) ensure that no two node bounding
7
- * boxes intersect after the call returns. Each pass builds its own Variables,
8
- * Rectangles, and Constraints; none are shared between passes.
9
- *
10
- * @see lib/neatogen/adjust.c:vpscAdjust
11
- * @see lib/vpsc/csolve_VPSC.h
12
- */
13
-
14
- import type { Node } from '../../model/node.js';
15
- import {
16
- newVariable,
17
- deleteVariable,
18
- deleteVPSC,
19
- deleteConstraints,
20
- solveVPSC,
21
- genXConstraints,
22
- genYConstraints,
23
- type Variable,
24
- type Constraint,
25
- VPSC,
26
- Rectangle,
27
- } from '../../vpsc/index.js';
28
-
29
- /**
30
- * Builds one Rectangle per node centred on pos[], expanding each half-size
31
- * by sep/2 so the VPSC gap `(w_i+w_j)/2` incorporates the padding.
32
- *
33
- * @see lib/neatogen/adjust.c:vpscAdjust
34
- */
35
- export function buildRectangles(
36
- nodes: Node[],
37
- sep: { x: number; y: number },
38
- ): Rectangle[] {
39
- return nodes.map((n) => {
40
- const x = n.info.pos?.[0] ?? 0;
41
- const y = n.info.pos?.[1] ?? 0;
42
- const hw = n.info.width / 2 + sep.x / 2;
43
- const hh = n.info.height / 2 + sep.y / 2;
44
- return new Rectangle(x - hw, x + hw, y - hh, y + hh);
45
- });
46
- }
47
-
48
- /**
49
- * Allocates one Variable per node for a single VPSC axis pass.
50
- *
51
- * @see lib/vpsc/csolve_VPSC.h:newVariable
52
- */
53
- export function allocateVariables(nodes: Node[], axis: 0 | 1): Variable[] {
54
- return nodes.map((n, i) => newVariable(i, n.info.pos?.[axis] ?? 0, 1));
55
- }
56
-
57
- /**
58
- * Writes solved positions back into node.info.pos[axis].
59
- *
60
- * @see lib/neatogen/adjust.c:vpscAdjust (position write-back loop)
61
- */
62
- export function writePositions(
63
- nodes: Node[],
64
- vars: Variable[],
65
- axis: 0 | 1,
66
- ): void {
67
- for (let i = 0; i < nodes.length; i++) {
68
- const n = nodes[i]!;
69
- if (!n.info.pos) n.info.pos = [0, 0];
70
- n.info.pos[axis] =
71
- vars[i]!.block !== null ? vars[i]!.position() : vars[i]!.desiredPosition;
72
- }
73
- }
74
-
75
- /**
76
- * Tears down a VPSC pass in mandatory order (AD-4):
77
- * deleteVPSC → deleteConstraints → deleteVariable per variable.
78
- *
79
- * @see lib/neatogen/adjust.c:vpscAdjust teardown sequence
80
- */
81
- export function teardownPass(
82
- vpsc: VPSC,
83
- constraints: Constraint[],
84
- vars: Variable[],
85
- ): void {
86
- deleteVPSC(vpsc);
87
- deleteConstraints(constraints);
88
- for (const v of vars) deleteVariable(v);
89
- }
90
-
91
- /**
92
- * Runs one complete VPSC axis pass: solve constraints then write positions.
93
- *
94
- * @see lib/neatogen/adjust.c:vpscAdjust
95
- */
96
- export function runAxisPass(
97
- nodes: Node[],
98
- sep: { x: number; y: number },
99
- constraints: Constraint[],
100
- vars: Variable[],
101
- axis: 0 | 1,
102
- ): void {
103
- const vpsc = new VPSC(vars, constraints);
104
- solveVPSC(vpsc);
105
- writePositions(nodes, vars, axis);
106
- teardownPass(vpsc, constraints, vars);
107
- }
108
-
109
- /**
110
- * One full VPSC pass on the X axis.
111
- *
112
- * @see lib/neatogen/adjust.c:vpscAdjust (X pass section)
113
- */
114
- export function runXPass(
115
- nodes: Node[],
116
- sep: { x: number; y: number },
117
- ): void {
118
- const rects = buildRectangles(nodes, sep);
119
- const vars = allocateVariables(nodes, 0);
120
- const cs = genXConstraints(rects, vars, true);
121
- runAxisPass(nodes, sep, cs, vars, 0);
122
- }
123
-
124
- /**
125
- * One full VPSC pass on the Y axis.
126
- *
127
- * @see lib/neatogen/adjust.c:vpscAdjust (Y pass section)
128
- */
129
- export function runYPass(
130
- nodes: Node[],
131
- sep: { x: number; y: number },
132
- ): void {
133
- const rects = buildRectangles(nodes, sep);
134
- const vars = allocateVariables(nodes, 1);
135
- const cs = genYConstraints(rects, vars);
136
- runAxisPass(nodes, sep, cs, vars, 1);
137
- }
138
-
139
- /**
140
- * Removes overlaps between nodes using two independent VPSC passes (X then Y).
141
- *
142
- * After this call no two node bounding boxes intersect (with sep padding).
143
- * Positions are written into node.info.pos[0] (X) and node.info.pos[1] (Y).
144
- *
145
- * @param nodes - Nodes with info.pos, info.width, info.height set.
146
- * @param sep - Minimum padding added beyond each node's half-size per axis.
147
- *
148
- * @see lib/neatogen/adjust.c:vpscAdjust
149
- * @see lib/vpsc/csolve_VPSC.h
150
- */
151
- export function removeOverlap(
152
- nodes: Node[],
153
- sep: { x: number; y: number },
154
- ): void {
155
- if (nodes.length < 2) return;
156
- runXPass(nodes, sep);
157
- runYPass(nodes, sep);
158
- }