@knowvah/dot-engine 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async/collect.d.ts.map +1 -1
- package/dist/async/render-async.d.ts.map +1 -1
- package/dist/common/arith.d.ts +6 -0
- package/dist/common/arith.d.ts.map +1 -1
- package/dist/common/poly-sizing.d.ts.map +1 -1
- package/dist/common/textmeasure-factory.d.ts +2 -1
- package/dist/common/textmeasure-factory.d.ts.map +1 -1
- package/dist/common/textmeasure.d.ts +1 -1
- package/dist/common/textmeasure.d.ts.map +1 -1
- package/dist/gvc/device-labels.d.ts +30 -0
- package/dist/gvc/device-labels.d.ts.map +1 -0
- package/dist/gvc/device-transform.d.ts +22 -0
- package/dist/gvc/device-transform.d.ts.map +1 -0
- package/dist/gvc/device.d.ts +2 -42
- package/dist/gvc/device.d.ts.map +1 -1
- package/dist/index.js +98 -79
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/graph-label.d.ts +10 -0
- package/dist/layout/dot/graph-label.d.ts.map +1 -1
- package/dist/layout/dot/splines-label.d.ts.map +1 -1
- package/dist/layout/fdp/normalize.d.ts.map +1 -1
- package/dist/layout/fdp/xlayout.d.ts +1 -1
- package/dist/layout/neato/dijkstra.d.ts +0 -32
- package/dist/layout/neato/dijkstra.d.ts.map +1 -1
- package/dist/layout/neato/fdp-adjust.d.ts +12 -1
- package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
- package/dist/layout/neato/index.d.ts +5 -5
- package/dist/layout/neato/index.d.ts.map +1 -1
- package/dist/layout/sfdp/index.d.ts.map +1 -1
- package/dist/render/public.d.ts +5 -0
- package/dist/render/public.d.ts.map +1 -1
- package/dist/render.js +95 -76
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/async/collect.ts +2 -14
- package/src/async/render-async.ts +7 -12
- package/src/common/arith.ts +9 -0
- package/src/common/poly-sizing.ts +2 -1
- package/src/common/textmeasure-factory.ts +20 -1
- package/src/common/textmeasure.ts +1 -1
- package/src/gvc/device-labels.ts +98 -0
- package/src/gvc/device-transform.ts +58 -0
- package/src/gvc/device.ts +8 -132
- package/src/layout/dot/graph-label.ts +6 -1
- package/src/layout/dot/splines-label.ts +2 -1
- package/src/layout/fdp/normalize.ts +2 -1
- package/src/layout/fdp/xlayout.ts +3 -2
- package/src/layout/neato/dijkstra.ts +0 -141
- package/src/layout/neato/fdp-adjust.ts +25 -3
- package/src/layout/neato/index.ts +13 -12
- package/src/layout/sfdp/index.ts +2 -1
- package/src/render/public.ts +6 -5
- package/dist/layout/neato/overlap.d.ts +0 -85
- package/dist/layout/neato/overlap.d.ts.map +0 -1
- 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
|
-
}
|
|
@@ -151,12 +151,26 @@ const ADJUST_MODES = new Map<string, readonly [number, string]>([
|
|
|
151
151
|
['porthoyx', [AM_PORTHOYX, 'yx pseudo-orthogonal constraints']],
|
|
152
152
|
]);
|
|
153
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
|
+
|
|
154
167
|
/**
|
|
155
168
|
* Resolve an `overlap` value to its adjust mode (the PRISM try count lands in
|
|
156
|
-
* `value`).
|
|
169
|
+
* `value`), warning as C does for an unrecognized value.
|
|
157
170
|
* @see lib/neatogen/adjust.c:getAdjustMode
|
|
158
171
|
*/
|
|
159
172
|
export function getAdjustMode(flag: string | undefined): AdjustData {
|
|
173
|
+
warnUnrecognizedOverlap(flag);
|
|
160
174
|
const ntry = overlapPrismTries(flag);
|
|
161
175
|
if (ntry !== null) return { mode: AM_PRISM, print: 'prism', value: ntry };
|
|
162
176
|
const named = ADJUST_MODES.get((flag ?? '').toLowerCase());
|
|
@@ -227,6 +241,14 @@ export function removeOverlapWith(g: Graph, am: AdjustData): number {
|
|
|
227
241
|
* @see lib/neatogen/adjust.c:adjustNodes / removeOverlapAs
|
|
228
242
|
*/
|
|
229
243
|
export function adjustNodesFull(g: Graph): number {
|
|
230
|
-
|
|
231
|
-
return removeOverlapWith(g,
|
|
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'));
|
|
232
254
|
}
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
MODEL_MDS,
|
|
26
26
|
} from './init.js';
|
|
27
27
|
import { neatoMode } from './start.js';
|
|
28
|
-
import {
|
|
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
|
|
104
|
-
*
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
package/src/layout/sfdp/index.ts
CHANGED
|
@@ -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.
|
package/src/render/public.ts
CHANGED
|
@@ -60,13 +60,14 @@ export interface RenderOptions {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// ---------------------------------------------------------------------------
|
|
63
|
-
//
|
|
63
|
+
// Argument validation (shared with the async entry points)
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
}
|