@knowvah/dot-engine 2.6.11 → 2.7.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 +1 -1
- package/dist/api.js +18 -4
- package/dist/api.js.map +3 -3
- package/dist/async/render-async.d.ts.map +1 -1
- package/dist/common/agerr.d.ts +37 -0
- package/dist/common/agerr.d.ts.map +1 -0
- package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
- package/dist/common/color.d.ts.map +1 -1
- package/dist/common/crand.d.ts +0 -20
- package/dist/common/crand.d.ts.map +1 -1
- package/dist/common/emit-once.d.ts +6 -0
- package/dist/common/emit-once.d.ts.map +1 -0
- package/dist/common/graph-init.d.ts.map +1 -1
- package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
- package/dist/common/htmltable-emit-fill.d.ts +29 -2
- package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
- package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
- package/dist/common/htmltable-emit.d.ts.map +1 -1
- package/dist/common/htmltable-lex-expat.d.ts +1 -6
- package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
- package/dist/common/htmltable-pos-runs.d.ts +7 -4
- package/dist/common/htmltable-pos-runs.d.ts.map +1 -1
- package/dist/common/htmltable.d.ts +1 -7
- package/dist/common/htmltable.d.ts.map +1 -1
- package/dist/common/libm-trig.d.ts +5 -0
- package/dist/common/libm-trig.d.ts.map +1 -0
- package/dist/common/multicolor.d.ts +21 -13
- package/dist/common/multicolor.d.ts.map +1 -1
- package/dist/common/poly-gencode.d.ts +1 -2
- package/dist/common/poly-gencode.d.ts.map +1 -1
- package/dist/common/poly-init.d.ts.map +1 -1
- package/dist/common/postproc.d.ts.map +1 -1
- package/dist/common/random.d.ts.map +1 -1
- package/dist/common/render-state.d.ts +8 -0
- package/dist/common/render-state.d.ts.map +1 -0
- package/dist/common/splines-clip.d.ts +7 -0
- package/dist/common/splines-clip.d.ts.map +1 -1
- package/dist/common/splines-geom.d.ts +8 -0
- package/dist/common/splines-geom.d.ts.map +1 -1
- package/dist/common/splines-routespl.d.ts.map +1 -1
- package/dist/common/textmeasure-lookup.d.ts.map +1 -1
- package/dist/common/textmeasure.d.ts.map +1 -1
- package/dist/gvc/anchor.d.ts.map +1 -1
- package/dist/gvc/context.d.ts +6 -0
- package/dist/gvc/context.d.ts.map +1 -1
- package/dist/gvc/device-cluster.d.ts +15 -0
- package/dist/gvc/device-cluster.d.ts.map +1 -1
- package/dist/gvc/device-labels.d.ts +14 -0
- package/dist/gvc/device-labels.d.ts.map +1 -1
- package/dist/gvc/device.d.ts.map +1 -1
- package/dist/gvc/first-layer.d.ts +9 -0
- package/dist/gvc/first-layer.d.ts.map +1 -0
- package/dist/gvc/gvrender-set-style.d.ts +28 -0
- package/dist/gvc/gvrender-set-style.d.ts.map +1 -0
- package/dist/gvc/usershape.d.ts +18 -0
- package/dist/gvc/usershape.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3169 -1296
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/cluster.d.ts.map +1 -1
- package/dist/layout/dot/compound.d.ts.map +1 -1
- package/dist/layout/dot/conc.d.ts.map +1 -1
- package/dist/layout/dot/decomp.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-clip.d.ts +32 -14
- package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-routing.d.ts.map +1 -1
- package/dist/layout/dot/mincross-build.d.ts.map +1 -1
- package/dist/layout/dot/mincross-cross.d.ts.map +1 -1
- package/dist/layout/dot/rank.d.ts.map +1 -1
- package/dist/layout/dot/splines.d.ts.map +1 -1
- package/dist/layout/fdp/index.d.ts.map +1 -1
- package/dist/layout/fdp/tlayout-parms.d.ts.map +1 -1
- package/dist/layout/fdp/xlayout.d.ts.map +1 -1
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts +11 -0
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts +36 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts +32 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts.map +1 -0
- package/dist/layout/neato/cdt-surface.d.ts.map +1 -1
- package/dist/layout/neato/compute-hierarchy.d.ts +24 -0
- package/dist/layout/neato/compute-hierarchy.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts +20 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad.d.ts +19 -0
- package/dist/layout/neato/conjgrad.d.ts.map +1 -1
- package/dist/layout/neato/constrained-majorization.d.ts +26 -0
- package/dist/layout/neato/constrained-majorization.d.ts.map +1 -0
- package/dist/layout/neato/dijkstra.d.ts +33 -16
- package/dist/layout/neato/dijkstra.d.ts.map +1 -1
- package/dist/layout/neato/edge-len.d.ts.map +1 -1
- package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
- package/dist/layout/neato/gts-triangle.d.ts +14 -0
- package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
- package/dist/layout/neato/init.d.ts +6 -22
- package/dist/layout/neato/init.d.ts.map +1 -1
- package/dist/layout/neato/kk.d.ts.map +1 -1
- package/dist/layout/neato/kkutils.d.ts +20 -0
- package/dist/layout/neato/kkutils.d.ts.map +1 -0
- package/dist/layout/neato/make-graph-data.d.ts +43 -0
- package/dist/layout/neato/make-graph-data.d.ts.map +1 -0
- package/dist/layout/neato/matrix-ops.d.ts +46 -0
- package/dist/layout/neato/matrix-ops.d.ts.map +1 -1
- package/dist/layout/neato/multispline-router.d.ts.map +1 -1
- package/dist/layout/neato/multispline.d.ts.map +1 -1
- package/dist/layout/neato/opt-arrangement.d.ts +20 -0
- package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
- package/dist/layout/neato/poly.d.ts.map +1 -1
- package/dist/layout/neato/quad-prog-solve.d.ts +44 -0
- package/dist/layout/neato/quad-prog-solve.d.ts.map +1 -0
- package/dist/layout/neato/sgd.d.ts.map +1 -1
- package/dist/layout/neato/smart-ini-x.d.ts +9 -0
- package/dist/layout/neato/smart-ini-x.d.ts.map +1 -0
- package/dist/layout/neato/splines.d.ts +1 -1
- package/dist/layout/neato/splines.d.ts.map +1 -1
- package/dist/layout/neato/start.d.ts +4 -1
- package/dist/layout/neato/start.d.ts.map +1 -1
- package/dist/layout/neato/stress.d.ts +8 -0
- package/dist/layout/neato/stress.d.ts.map +1 -1
- package/dist/model/edge.d.ts +1 -1
- package/dist/model/edge.d.ts.map +1 -1
- package/dist/model/lazy-attrs.d.ts +10 -0
- package/dist/model/lazy-attrs.d.ts.map +1 -0
- package/dist/model/node.d.ts +1 -1
- package/dist/model/node.d.ts.map +1 -1
- package/dist/ortho/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/render/color-resolve.d.ts +49 -1
- package/dist/render/color-resolve.d.ts.map +1 -1
- package/dist/render/dot/edge-draw.d.ts +1 -1
- package/dist/render/dot/edge-draw.d.ts.map +1 -1
- package/dist/render/public.d.ts.map +1 -1
- package/dist/render/svg-edge-split.d.ts +2 -0
- package/dist/render/svg-edge-split.d.ts.map +1 -1
- package/dist/render/svg-multicolor.d.ts +2 -2
- package/dist/render/svg-multicolor.d.ts.map +1 -1
- package/dist/render/xdot-public.d.ts.map +1 -1
- package/dist/render.js +2799 -927
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/async/render-async.ts +6 -0
- package/src/common/agerr.ts +96 -0
- package/src/common/arrows-shapes-poly.ts +19 -5
- package/src/common/color.ts +38 -10
- package/src/common/crand.ts +8 -2
- package/src/common/emit-once.ts +27 -0
- package/src/common/graph-init.ts +2 -1
- package/src/common/htmltable-attr-warn.ts +3 -4
- package/src/common/htmltable-emit-fill.ts +104 -11
- package/src/common/htmltable-emit-rules.ts +2 -0
- package/src/common/htmltable-emit.ts +4 -3
- package/src/common/htmltable-lex-expat.ts +96 -23
- package/src/common/htmltable-pos-runs.ts +11 -6
- package/src/common/htmltable.ts +28 -117
- package/src/common/libm-trig.ts +43 -0
- package/src/common/multicolor.ts +88 -24
- package/src/common/poly-gencode.ts +30 -10
- package/src/common/poly-init.ts +20 -1
- package/src/common/postproc.ts +4 -1
- package/src/common/random.ts +11 -1
- package/src/common/render-state.ts +36 -0
- package/src/common/splines-clip.ts +15 -12
- package/src/common/splines-geom.ts +8 -4
- package/src/common/splines-routespl.ts +2 -1
- package/src/common/textmeasure-lookup.ts +9 -2
- package/src/common/textmeasure.ts +45 -2
- package/src/gvc/anchor.ts +1 -12
- package/src/gvc/context.ts +8 -0
- package/src/gvc/device-cluster.ts +93 -10
- package/src/gvc/device-labels.ts +27 -0
- package/src/gvc/device.ts +34 -8
- package/src/gvc/first-layer.ts +26 -0
- package/src/gvc/gvrender-set-style.ts +114 -0
- package/src/gvc/usershape.ts +26 -0
- package/src/index.ts +3 -0
- package/src/layout/dot/cluster.ts +2 -1
- package/src/layout/dot/compound.ts +3 -4
- package/src/layout/dot/conc.ts +6 -5
- package/src/layout/dot/decomp.ts +3 -0
- package/src/layout/dot/edge-route-clip.ts +46 -28
- package/src/layout/dot/edge-route-routing.ts +11 -3
- package/src/layout/dot/fastgr.ts +2 -2
- package/src/layout/dot/mincross-build.ts +2 -0
- package/src/layout/dot/mincross-cross.ts +2 -0
- package/src/layout/dot/rank.ts +2 -0
- package/src/layout/dot/splines.ts +2 -1
- package/src/layout/fdp/index.ts +2 -1
- package/src/layout/fdp/tlayout-parms.ts +9 -2
- package/src/layout/fdp/xlayout.ts +10 -2
- package/src/layout/neato/__fixtures__/dijkstra-f.ts +17 -0
- package/src/layout/neato/__fixtures__/hier/c1554.gv +33 -0
- package/src/layout/neato/__fixtures__/hier/c1554.plain +26 -0
- package/src/layout/neato/__fixtures__/hier/c1554.xdot +161 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.plain +11 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.plain +16 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.xdot +62 -0
- package/src/layout/neato/__fixtures__/hier-imds.ts +151 -0
- package/src/layout/neato/__fixtures__/hier-majorization.ts +145 -0
- package/src/layout/neato/cdt-surface.ts +12 -22
- package/src/layout/neato/compute-hierarchy.ts +90 -0
- package/src/layout/neato/conjgrad-mkernel.ts +141 -0
- package/src/layout/neato/conjgrad.ts +208 -0
- package/src/layout/neato/constrained-majorization.ts +479 -0
- package/src/layout/neato/dijkstra.ts +218 -92
- package/src/layout/neato/edge-len.ts +2 -1
- package/src/layout/neato/fdp-adjust.ts +3 -2
- package/src/layout/neato/gts-triangle.ts +42 -0
- package/src/layout/neato/init.ts +68 -68
- package/src/layout/neato/kk.ts +2 -1
- package/src/layout/neato/kkutils.ts +96 -0
- package/src/layout/neato/make-graph-data.ts +166 -0
- package/src/layout/neato/matrix-ops.ts +138 -0
- package/src/layout/neato/multispline-router.ts +3 -3
- package/src/layout/neato/multispline.ts +3 -2
- package/src/layout/neato/opt-arrangement.ts +86 -0
- package/src/layout/neato/poly.ts +3 -2
- package/src/layout/neato/quad-prog-solve.ts +489 -0
- package/src/layout/neato/sgd.ts +5 -2
- package/src/layout/neato/smart-ini-x.ts +503 -0
- package/src/layout/neato/splines.ts +4 -4
- package/src/layout/neato/start.ts +19 -14
- package/src/layout/neato/stress.ts +28 -0
- package/src/model/edge.ts +3 -2
- package/src/model/lazy-attrs.ts +36 -0
- package/src/model/node.ts +3 -2
- package/src/ortho/index.ts +2 -1
- package/src/parser/dot.js +4 -1
- package/src/parser/dot.pegjs +5 -1
- package/src/parser/index.ts +4 -3
- package/src/render/color-resolve.ts +193 -4
- package/src/render/dot/edge-draw.ts +6 -3
- package/src/render/dot.ts +1 -1
- package/src/render/public.ts +2 -0
- package/src/render/svg-edge-split.ts +6 -4
- package/src/render/svg-multicolor.ts +4 -3
- package/src/render/svg-parallel-edge.ts +2 -2
- package/src/render/xdot-public.ts +2 -0
|
@@ -30,126 +30,252 @@ export interface TermSgd {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
|
-
//
|
|
33
|
+
// Float Dijkstra: dijkstra_f and its indexed heap (shares NgHeap and the
|
|
34
|
+
// exchange / assign helpers with the DistType heap below)
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
|
|
36
|
-
/**
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let pos = heap.length;
|
|
43
|
-
heap.push(idx);
|
|
44
|
-
while (pos > 0) {
|
|
45
|
-
const parent = (pos - 1) >> 1;
|
|
46
|
-
if (dist[heap[parent]] <= dist[heap[pos]]) break;
|
|
47
|
-
const tmp = heap[parent];
|
|
48
|
-
heap[parent] = heap[pos];
|
|
49
|
-
heap[pos] = tmp;
|
|
50
|
-
pos = parent;
|
|
51
|
-
}
|
|
37
|
+
/** C `FLT_MAX`: the "unreachable" distance of dijkstra_f. */
|
|
38
|
+
export const FLT_MAX = 3.4028234663852886e38;
|
|
39
|
+
|
|
40
|
+
/** @see lib/neatogen/dijkstra.c:greaterPriority_f */
|
|
41
|
+
function greaterPriorityF(h: NgHeap, i: number, j: number, dist: Float32Array): boolean {
|
|
42
|
+
return dist[h.data[i]!]! < dist[h.data[j]!]!;
|
|
52
43
|
}
|
|
53
44
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
let
|
|
45
|
+
/** @see lib/neatogen/dijkstra.c:heapify_f */
|
|
46
|
+
function heapifyF(h: NgHeap, start: number, dist: Float32Array): void {
|
|
47
|
+
let i = start;
|
|
57
48
|
for (;;) {
|
|
58
|
-
const l =
|
|
59
|
-
const r =
|
|
60
|
-
let
|
|
61
|
-
if (
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
heap[smallest] = heap[pos];
|
|
66
|
-
heap[pos] = tmp;
|
|
67
|
-
pos = smallest;
|
|
49
|
+
const l = ngLeft(i);
|
|
50
|
+
const r = ngRight(i);
|
|
51
|
+
let largest = ngInsideHeap(h, l) && greaterPriorityF(h, l, i, dist) ? l : i;
|
|
52
|
+
if (ngInsideHeap(h, r) && greaterPriorityF(h, r, largest, dist)) largest = r;
|
|
53
|
+
if (largest === i) break;
|
|
54
|
+
ngExchange(h, largest, i);
|
|
55
|
+
i = largest;
|
|
68
56
|
}
|
|
69
57
|
}
|
|
70
58
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Build a min-heap from all nodes except `src`. @internal */
|
|
82
|
-
export function heapBuildExcluding(
|
|
83
|
-
src: number,
|
|
84
|
-
n: number,
|
|
85
|
-
dist: Float32Array,
|
|
86
|
-
): number[] {
|
|
87
|
-
const heap: number[] = [];
|
|
59
|
+
/** @see lib/neatogen/dijkstra.c:initHeap_f */
|
|
60
|
+
function initHeapF(startVertex: number, dist: Float32Array, n: number): NgHeap {
|
|
61
|
+
const h: NgHeap = {
|
|
62
|
+
data: new Array<number>(Math.max(n - 1, 0)).fill(0),
|
|
63
|
+
heapSize: n - 1,
|
|
64
|
+
index: new Int32Array(n),
|
|
65
|
+
};
|
|
66
|
+
let count = 0;
|
|
88
67
|
for (let i = 0; i < n; i++) {
|
|
89
|
-
if (i !==
|
|
68
|
+
if (i !== startVertex) {
|
|
69
|
+
h.data[count] = i;
|
|
70
|
+
h.index[i] = count;
|
|
71
|
+
count++;
|
|
72
|
+
}
|
|
90
73
|
}
|
|
91
|
-
|
|
74
|
+
for (let j = Math.trunc((n - 1) / 2); j >= 0; j--) heapifyF(h, j, dist);
|
|
75
|
+
return h;
|
|
92
76
|
}
|
|
93
77
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Pops the closest vertex; returns -1 (C `false`) when the heap is empty.
|
|
80
|
+
* @see lib/neatogen/dijkstra.c:extractMax_f
|
|
81
|
+
*/
|
|
82
|
+
function extractMaxF(h: NgHeap, dist: Float32Array): number {
|
|
83
|
+
if (h.heapSize === 0) return -1;
|
|
84
|
+
const max = h.data[0]!;
|
|
85
|
+
h.data[0] = h.data[h.heapSize - 1]!;
|
|
86
|
+
h.index[h.data[0]] = 0;
|
|
87
|
+
h.heapSize--;
|
|
88
|
+
heapifyF(h, 0, dist);
|
|
89
|
+
return max;
|
|
90
|
+
}
|
|
97
91
|
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
graph: VtxData[],
|
|
102
|
-
n: number,
|
|
103
|
-
dist: Float32Array,
|
|
92
|
+
/** @see lib/neatogen/dijkstra.c:increaseKey_f */
|
|
93
|
+
function increaseKeyF(
|
|
94
|
+
h: NgHeap, increasedVertex: number, newDist: number, dist: Float32Array,
|
|
104
95
|
): void {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
if (dist[increasedVertex]! <= newDist) return;
|
|
97
|
+
let i = h.index[increasedVertex]!;
|
|
98
|
+
dist[increasedVertex] = newDist;
|
|
99
|
+
while (i > 0 && dist[h.data[ngParent(i)]!]! > newDist) {
|
|
100
|
+
ngAssign(h, i, ngParent(i));
|
|
101
|
+
i = ngParent(i);
|
|
102
|
+
}
|
|
103
|
+
h.data[i] = increasedVertex;
|
|
104
|
+
h.index[increasedVertex] = i;
|
|
109
105
|
}
|
|
110
106
|
|
|
111
|
-
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Weighted single-source shortest paths from `vertex`; assumes a connected
|
|
109
|
+
* graph, unreachable vertices keep `FLT_MAX`. All arithmetic is C `float`.
|
|
110
|
+
*
|
|
111
|
+
* @see lib/neatogen/dijkstra.c:dijkstra_f
|
|
112
|
+
*/
|
|
113
|
+
export function dijkstra(
|
|
114
|
+
vertex: number,
|
|
115
115
|
graph: VtxData[],
|
|
116
|
+
n: number,
|
|
116
117
|
dist: Float32Array,
|
|
117
|
-
heap: number[],
|
|
118
118
|
): void {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
for (let i = 0; i < n; i++) dist[i] = FLT_MAX;
|
|
120
|
+
dist[vertex] = 0;
|
|
121
|
+
const vd = graph[vertex]!;
|
|
122
|
+
for (let i = 1; i < vd.nedges; i++) dist[vd.edges[i]!] = vd.ewgts[i]!;
|
|
123
|
+
|
|
124
|
+
const h = initHeapF(vertex, dist, n);
|
|
125
|
+
for (let closest = extractMaxF(h, dist); closest >= 0; closest = extractMaxF(h, dist)) {
|
|
126
|
+
const closestDist = dist[closest]!;
|
|
127
|
+
if (closestDist === FLT_MAX) break;
|
|
128
|
+
const cd = graph[closest]!;
|
|
129
|
+
for (let i = 1; i < cd.nedges; i++) {
|
|
130
|
+
increaseKeyF(h, cd.edges[i]!, Math.fround(closestDist + Math.fround(cd.ewgts[i]!)), dist);
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
// ---------------------------------------------------------------------------
|
|
131
|
-
//
|
|
136
|
+
// Integer (DistType) Dijkstra: ngdijkstra and its max-heap
|
|
132
137
|
// ---------------------------------------------------------------------------
|
|
133
138
|
|
|
139
|
+
/** C `(DistType)INT_MAX`. @see lib/neatogen/dijkstra.c:MAX_DIST */
|
|
140
|
+
export const MAX_DIST = 2147483647;
|
|
141
|
+
|
|
134
142
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* @see lib/neatogen/dijkstra.c:dijkstra_f
|
|
143
|
+
* The heap of dijkstra.c: `data[i] = vertexNum <=> index[vertexNum] = i`, with
|
|
144
|
+
* the 0-rooted `left = 2i`, `right = 2i + 1`, `parent = i / 2` numbering.
|
|
145
|
+
* @see lib/neatogen/dijkstra.c:heap
|
|
140
146
|
*/
|
|
141
|
-
export
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
export interface NgHeap {
|
|
148
|
+
data: number[];
|
|
149
|
+
heapSize: number;
|
|
150
|
+
index: Int32Array;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** @see lib/neatogen/dijkstra.c:left */
|
|
154
|
+
function ngLeft(i: number): number { return 2 * i; }
|
|
155
|
+
|
|
156
|
+
/** @see lib/neatogen/dijkstra.c:right */
|
|
157
|
+
function ngRight(i: number): number { return 2 * i + 1; }
|
|
158
|
+
|
|
159
|
+
/** @see lib/neatogen/dijkstra.c:parent */
|
|
160
|
+
function ngParent(i: number): number { return Math.trunc(i / 2); }
|
|
161
|
+
|
|
162
|
+
/** @see lib/neatogen/dijkstra.c:insideHeap */
|
|
163
|
+
function ngInsideHeap(h: NgHeap, i: number): boolean { return i < h.heapSize; }
|
|
164
|
+
|
|
165
|
+
/** @see lib/neatogen/dijkstra.c:greaterPriority */
|
|
166
|
+
function ngGreaterPriority(h: NgHeap, i: number, j: number, dist: Int32Array): boolean {
|
|
167
|
+
return dist[h.data[i]!]! < dist[h.data[j]!]!;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @see lib/neatogen/dijkstra.c:assign */
|
|
171
|
+
function ngAssign(h: NgHeap, i: number, j: number): void {
|
|
172
|
+
h.data[i] = h.data[j]!;
|
|
173
|
+
h.index[h.data[i]!] = i;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @see lib/neatogen/dijkstra.c:exchange */
|
|
177
|
+
function ngExchange(h: NgHeap, i: number, j: number): void {
|
|
178
|
+
const t = h.data[i]!;
|
|
179
|
+
h.data[i] = h.data[j]!;
|
|
180
|
+
h.data[j] = t;
|
|
181
|
+
h.index[h.data[i]!] = i;
|
|
182
|
+
h.index[h.data[j]!] = j;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** @see lib/neatogen/dijkstra.c:heapify */
|
|
186
|
+
function ngHeapify(h: NgHeap, start: number, dist: Int32Array): void {
|
|
187
|
+
let i = start;
|
|
188
|
+
for (;;) {
|
|
189
|
+
const l = ngLeft(i);
|
|
190
|
+
const r = ngRight(i);
|
|
191
|
+
let largest = ngInsideHeap(h, l) && ngGreaterPriority(h, l, i, dist) ? l : i;
|
|
192
|
+
if (ngInsideHeap(h, r) && ngGreaterPriority(h, r, largest, dist)) largest = r;
|
|
193
|
+
if (largest === i) break;
|
|
194
|
+
ngExchange(h, largest, i);
|
|
195
|
+
i = largest;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @see lib/neatogen/dijkstra.c:initHeap */
|
|
200
|
+
export function ngInitHeap(startVertex: number, dist: Int32Array, n: number): NgHeap {
|
|
201
|
+
const h: NgHeap = {
|
|
202
|
+
data: new Array<number>(Math.max(n - 1, 0)).fill(0),
|
|
203
|
+
heapSize: n - 1,
|
|
204
|
+
index: new Int32Array(n),
|
|
205
|
+
};
|
|
206
|
+
let count = 0;
|
|
207
|
+
for (let i = 0; i < n; i++) {
|
|
208
|
+
if (i !== startVertex) {
|
|
209
|
+
h.data[count] = i;
|
|
210
|
+
h.index[i] = count;
|
|
211
|
+
count++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
for (let j = Math.trunc((n - 1) / 2); j >= 0; j--) ngHeapify(h, j, dist);
|
|
215
|
+
return h;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Pops the closest vertex; returns -1 (C `false`) when the heap is empty.
|
|
220
|
+
* @see lib/neatogen/dijkstra.c:extractMax
|
|
221
|
+
*/
|
|
222
|
+
export function ngExtractMax(h: NgHeap, dist: Int32Array): number {
|
|
223
|
+
if (h.heapSize === 0) return -1;
|
|
224
|
+
const max = h.data[0]!;
|
|
225
|
+
h.data[0] = h.data[h.heapSize - 1]!;
|
|
226
|
+
h.index[h.data[0]] = 0;
|
|
227
|
+
h.heapSize--;
|
|
228
|
+
ngHeapify(h, 0, dist);
|
|
229
|
+
return max;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** @see lib/neatogen/dijkstra.c:increaseKey */
|
|
233
|
+
export function ngIncreaseKey(
|
|
234
|
+
h: NgHeap, increasedVertex: number, newDist: number, dist: Int32Array,
|
|
146
235
|
): void {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
236
|
+
if (dist[increasedVertex]! <= newDist) return;
|
|
237
|
+
let i = h.index[increasedVertex]!;
|
|
238
|
+
dist[increasedVertex] = newDist;
|
|
239
|
+
while (i > 0 && dist[h.data[ngParent(i)]!]! > newDist) {
|
|
240
|
+
ngAssign(h, i, ngParent(i));
|
|
241
|
+
i = ngParent(i);
|
|
242
|
+
}
|
|
243
|
+
h.data[i] = increasedVertex;
|
|
244
|
+
h.index[increasedVertex] = i;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** C `(DistType)` of a float edge weight (truncation toward zero). */
|
|
248
|
+
function toDistType(w: number): number {
|
|
249
|
+
return Math.trunc(Math.fround(w));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Single-source shortest paths on the int-truncated edge weights `ewgts`;
|
|
254
|
+
* unreachable vertices get `prevClosestDist + 10` (INT_MAX when that would
|
|
255
|
+
* overflow).
|
|
256
|
+
* @see lib/neatogen/dijkstra.c:ngdijkstra
|
|
257
|
+
*/
|
|
258
|
+
export function ngdijkstra(vertex: number, graph: VtxData[], n: number, dist: Int32Array): void {
|
|
259
|
+
let prevClosestDist = MAX_DIST;
|
|
260
|
+
for (let i = 0; i < n; i++) dist[i] = MAX_DIST;
|
|
261
|
+
dist[vertex] = 0;
|
|
262
|
+
const vd = graph[vertex]!;
|
|
263
|
+
for (let i = 1; i < vd.nedges; i++) dist[vd.edges[i]!] = toDistType(vd.ewgts[i]!);
|
|
264
|
+
|
|
265
|
+
const h = ngInitHeap(vertex, dist, n);
|
|
266
|
+
for (let closest = ngExtractMax(h, dist); closest >= 0; closest = ngExtractMax(h, dist)) {
|
|
267
|
+
const closestDist = dist[closest]!;
|
|
268
|
+
if (closestDist === MAX_DIST) break;
|
|
269
|
+
const cd = graph[closest]!;
|
|
270
|
+
for (let i = 1; i < cd.nedges; i++) {
|
|
271
|
+
ngIncreaseKey(h, cd.edges[i]!, (closestDist + toDistType(cd.ewgts[i]!)) | 0, dist);
|
|
272
|
+
}
|
|
273
|
+
prevClosestDist = closestDist;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
for (let i = 0; i < n; i++) {
|
|
277
|
+
if (dist[i] === MAX_DIST) {
|
|
278
|
+
dist[i] = MAX_DIST - prevClosestDist < 10 ? MAX_DIST : prevClosestDist + 10;
|
|
279
|
+
}
|
|
154
280
|
}
|
|
155
281
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { Graph } from '../../model/graph.js';
|
|
11
11
|
import type { Edge } from '../../model/edge.js';
|
|
12
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
12
13
|
|
|
13
14
|
/** `dfltlen` passed by scan_graph_mode. @see lib/neatogen/stuff.c:scan_graph_mode */
|
|
14
15
|
export const DEFAULT_EDGE_LEN = 1.0;
|
|
@@ -24,7 +25,7 @@ export function lenattr(ep: Edge, g: Graph): number {
|
|
|
24
25
|
const val = /^\s*[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/.exec(s);
|
|
25
26
|
const len = val === null ? Number.NaN : parseFloat(val[0]);
|
|
26
27
|
if (Number.isNaN(len) || len < 0 || len === 0) {
|
|
27
|
-
|
|
28
|
+
agwarningf(`bad edge len "${s}" in ${g.name} - setting to ${DEFAULT_EDGE_LEN.toFixed(2)}`);
|
|
28
29
|
return DEFAULT_EDGE_LEN;
|
|
29
30
|
}
|
|
30
31
|
return len;
|
|
@@ -26,6 +26,7 @@ import { vpscAdjust } from './vpsc-adjust.js';
|
|
|
26
26
|
import { sepFactor } from './sep-factor.js';
|
|
27
27
|
import { lateDouble } from '../../common/nodeinit.js';
|
|
28
28
|
import { RenderError } from '../../errors.js';
|
|
29
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
29
30
|
|
|
30
31
|
/** @see lib/neatogen/adjust.c:DFLT_MARGIN (points) */
|
|
31
32
|
const DFLT_MARGIN = 4;
|
|
@@ -161,7 +162,7 @@ export function warnUnrecognizedOverlap(flag: string | undefined): void {
|
|
|
161
162
|
const s = flag.toLowerCase();
|
|
162
163
|
if (s.startsWith('prism') || NAMED_MODES.has(s)) return;
|
|
163
164
|
if (mapBoolDflt(flag, false) === mapBoolDflt(flag, true)) return;
|
|
164
|
-
|
|
165
|
+
agwarningf(`Unrecognized overlap value "${flag}" - using false`);
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
/**
|
|
@@ -203,7 +204,7 @@ function adjustLate(g: Graph, am: AdjustData): number {
|
|
|
203
204
|
case AM_IPSEP: return 0;
|
|
204
205
|
case AM_VPSC: return vpscAdjust(g);
|
|
205
206
|
default:
|
|
206
|
-
|
|
207
|
+
agwarningf(`Unhandled adjust option ${am.print}`);
|
|
207
208
|
return 0;
|
|
208
209
|
}
|
|
209
210
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* GTS's enclosing triangle, split from cdt-surface.ts to mirror the GTS source
|
|
4
|
+
* file it ports (the oracle links libgts 0.7.6 for neato's constrained
|
|
5
|
+
* Delaunay triangulation).
|
|
6
|
+
* @see gts-0.7.6 src/triangle.c
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { fma } from '../../common/fma.js';
|
|
10
|
+
|
|
11
|
+
/** `#define SQRT3 1.73205080757` (gts-0.7.6 src/triangle.c). */
|
|
12
|
+
const SQRT3 = 1.73205080757;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Vertices of the triangle enclosing points (px[0..n), py[0..n)). The oracle's
|
|
16
|
+
* libgts (arm64) fuses r*SQRT3 + xo, r*2 + yo, xo - r*SQRT3 and
|
|
17
|
+
* dx*dx + dy*dy (fmadd/fmsub in _gts_triangle_enclosing), so those are fma.
|
|
18
|
+
* @see gts-0.7.6 src/triangle.c:gts_triangle_enclosing
|
|
19
|
+
*/
|
|
20
|
+
export function gtsTriangleEnclosing(
|
|
21
|
+
px: number[], py: number[], n: number, scale: number,
|
|
22
|
+
): [[number, number], [number, number], [number, number]] {
|
|
23
|
+
let xmin = px[0]!; let xmax = px[0]!;
|
|
24
|
+
let ymin = py[0]!; let ymax = py[0]!;
|
|
25
|
+
for (let i = 1; i < n; i++) {
|
|
26
|
+
if (px[i]! > xmax) xmax = px[i]!;
|
|
27
|
+
else if (px[i]! < xmin) xmin = px[i]!;
|
|
28
|
+
if (py[i]! > ymax) ymax = py[i]!;
|
|
29
|
+
else if (py[i]! < ymin) ymin = py[i]!;
|
|
30
|
+
}
|
|
31
|
+
const xo = (xmax + xmin) / 2;
|
|
32
|
+
const yo = (ymax + ymin) / 2;
|
|
33
|
+
const dx = xmax - xo;
|
|
34
|
+
const dy = ymax - yo;
|
|
35
|
+
let r = scale * Math.sqrt(fma(dx, dx, dy * dy));
|
|
36
|
+
if (r === 0) r = scale;
|
|
37
|
+
return [
|
|
38
|
+
[fma(r, SQRT3, xo), yo - r],
|
|
39
|
+
[xo, fma(r, 2, yo)],
|
|
40
|
+
[fma(-r, SQRT3, xo), yo - r],
|
|
41
|
+
];
|
|
42
|
+
}
|
package/src/layout/neato/init.ts
CHANGED
|
@@ -16,15 +16,22 @@ import type { Edge } from '../../model/edge.js';
|
|
|
16
16
|
import type { VtxData } from './dijkstra.js';
|
|
17
17
|
import {
|
|
18
18
|
DFLT_ITERATIONS,
|
|
19
|
+
OPT_SMART_INIT as STRESS_OPT_SMART_INIT,
|
|
19
20
|
MODEL_MDS as STRESS_MODEL_MDS,
|
|
20
21
|
MODEL_CIRCUIT as STRESS_MODEL_CIRCUIT,
|
|
21
22
|
MODEL_SUBSET as STRESS_MODEL_SUBSET,
|
|
22
23
|
} from './stress.js';
|
|
23
24
|
import { sgdLayout } from './sgd.js';
|
|
24
25
|
import { kkNeato } from './kk.js';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
import {
|
|
27
|
+
checkStart, assertSupported, parseStart, INIT_RANDOM, INIT_REGULAR, INIT_SELF,
|
|
28
|
+
} from './start.js';
|
|
29
|
+
import { makeGraphDataC, graphHasLen } from './make-graph-data.js';
|
|
30
|
+
import type { DirectedVtxData } from './opt-arrangement.js';
|
|
31
|
+
import { stressMajorizationWithHierarchy } from './constrained-majorization.js';
|
|
32
|
+
import { agerr } from '../../common/agerr.js';
|
|
33
|
+
|
|
34
|
+
export { makeGraphDataC, graphHasLen, checkStart };
|
|
28
35
|
import { mapbool } from '../dot/rank.js';
|
|
29
36
|
import {
|
|
30
37
|
computeApspPacked,
|
|
@@ -50,6 +57,7 @@ export const MODE_HIER = 2;
|
|
|
50
57
|
export const MODE_IPSEP = 3;
|
|
51
58
|
/** Stochastic gradient descent. @see lib/neatogen/neatoprocs.h */
|
|
52
59
|
export const MODE_SGD = 4;
|
|
60
|
+
/** `MaxIter` under MODE_SGD. @see neatoinit.c:1285 */ const SGD_ITERATIONS = 30;
|
|
53
61
|
|
|
54
62
|
// ---------------------------------------------------------------------------
|
|
55
63
|
// Model constants — @see lib/neatogen/defs.h
|
|
@@ -321,79 +329,75 @@ export function mapModelToStress(model: number): number {
|
|
|
321
329
|
// solveModel helpers
|
|
322
330
|
// ---------------------------------------------------------------------------
|
|
323
331
|
|
|
324
|
-
/**
|
|
325
|
-
export function
|
|
326
|
-
|
|
327
|
-
|
|
332
|
+
/** `MaxIter` per mode; `maxiter` goes through C `atoi` (no digits -> 0). @see neatoinit.c:1280-1287 */
|
|
333
|
+
export function neatoMaxIter(maxiter: string | undefined, mode: number, n: number): number {
|
|
334
|
+
if (maxiter !== undefined) return parseInt(maxiter, 10) || 0;
|
|
335
|
+
if (mode === MODE_MAJOR) return DFLT_ITERATIONS;
|
|
336
|
+
return mode === MODE_SGD ? SGD_ITERATIONS : 100 * n;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** `late_double(g, "levelsgap", 0.0, -DBL_MAX)`. @see lib/neatogen/neatoinit.c:majorization */
|
|
340
|
+
const levelsGapOf = (g: Graph): number =>
|
|
341
|
+
lateDouble(g.root.attrs.get('levelsgap'), 0.0, -Number.MAX_VALUE);
|
|
342
|
+
|
|
343
|
+
/** What `majorization` was called with, plus the options derived from it. */
|
|
344
|
+
interface MajorizationCtx {
|
|
345
|
+
readonly g: Graph;
|
|
346
|
+
readonly mode: number;
|
|
347
|
+
readonly model: number;
|
|
348
|
+
readonly haveLen: boolean;
|
|
349
|
+
/** `opt_*` bits from `checkExp` and `checkStart`. */
|
|
350
|
+
readonly opts: number;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* The solver step of `majorization`: `stress_majorization_with_hierarchy` for
|
|
355
|
+
* MODE_HIER, else the kD_mkernel (MODE_IPSEP without constraints runs it too).
|
|
356
|
+
* Returns C's `rv`; the coordinates are left in `dCoords`.
|
|
357
|
+
* @see lib/neatogen/neatoinit.c:majorization
|
|
358
|
+
*/
|
|
359
|
+
function solveStress(
|
|
360
|
+
c: MajorizationCtx, vtx: DirectedVtxData[], dCoords: Float64Array[], nodeList: Node[],
|
|
361
|
+
): number {
|
|
362
|
+
const { g, mode, model, haveLen, opts } = c;
|
|
328
363
|
const n = nodeList.length;
|
|
329
|
-
const
|
|
330
|
-
const vtx = makeGraphDataC(g, nodeList, haveLen, mode === MODE_HIER || mode === MODE_IPSEP);
|
|
331
|
-
checkStart(g); // C: srand48(seed) immediately before initLayout draws
|
|
332
|
-
const maxi = lateDouble(g.attrs.get('maxiter'), DFLT_ITERATIONS, 0);
|
|
364
|
+
const maxi = neatoMaxIter(g.attrs.get('maxiter'), mode, n);
|
|
333
365
|
const epsilon = lateDouble(g.attrs.get('epsilon'), 1e-4, 0); // DFLT_TOLERANCE
|
|
366
|
+
if (mode === MODE_HIER) {
|
|
367
|
+
return stressMajorizationWithHierarchy(vtx, n, dCoords, nodeList, {
|
|
368
|
+
dim: DFLT_DIM, opts, model, maxi, epsilon, levelsGap: levelsGapOf(g),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
334
371
|
// C ipsep (stress_majorization_cola, constrained_majorization_ipsep.c:150) always uses
|
|
335
372
|
// compute_apsp_packed; kD_mkernel uses the weighted APSP iff graph->ewgts (stress.c:855).
|
|
336
373
|
const weighted = haveLen && mode !== MODE_IPSEP;
|
|
337
374
|
const Dij = weighted ? computeWeightedApspPacked(vtx, n) : computeApspPacked(vtx, n);
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
dim: DFLT_DIM, opts: OPT_EXP_FLAG, maxi, epsilon,
|
|
375
|
+
return stressMajorizationKDMkernel(Dij, n, dCoords, nodeList, {
|
|
376
|
+
dim: DFLT_DIM, opts, maxi, epsilon,
|
|
341
377
|
});
|
|
342
|
-
for (let i = 0; i < n; i++) {
|
|
343
|
-
nodeList[i]!.info.pos = [dCoords[0]![i]!, dCoords[1]![i]!];
|
|
344
|
-
}
|
|
345
378
|
}
|
|
346
379
|
|
|
347
|
-
/**
|
|
348
|
-
export function
|
|
349
|
-
|
|
350
|
-
|
|
380
|
+
/** Run stress majorization and write positions back. @see lib/neatogen/neatoinit.c:majorization */
|
|
381
|
+
export function runMajorization(g: Graph, mode: number, model: number): void {
|
|
382
|
+
const nodeList = assignNodeIds(g);
|
|
383
|
+
const n = nodeList.length;
|
|
384
|
+
const haveLen = graphHasLen(g);
|
|
385
|
+
// C: srand48(seed) immediately before initLayout draws; hier starts from self
|
|
386
|
+
const init = checkStart(g, n, mode === MODE_HIER ? INIT_SELF : INIT_RANDOM);
|
|
387
|
+
const opts = init === INIT_SELF ? OPT_EXP_FLAG | STRESS_OPT_SMART_INIT : OPT_EXP_FLAG;
|
|
388
|
+
const haveDir = mode === MODE_HIER || mode === MODE_IPSEP;
|
|
389
|
+
const vtx = makeGraphDataC(g, nodeList, haveLen, haveDir, mode);
|
|
390
|
+
const dCoords = [new Float64Array(n), new Float64Array(n)];
|
|
391
|
+
if (solveStress({ g, mode, model, haveLen, opts }, vtx, dCoords, nodeList) < 0) {
|
|
392
|
+
agerr('prev', 'layout aborted');
|
|
393
|
+
return;
|
|
351
394
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* C makeGraphData's vtx_data: per node out-edges then in-edges (agfstedge order), self at
|
|
357
|
-
* index 0, duplicate neighbours merged (len keeps the max). ewgts: lens (default 1.0) if any
|
|
358
|
-
* len attr, else 1.0 under haveDir (hier/ipsep), else absent (C NULL). eweights (haveWt)
|
|
359
|
-
* and edists are not built: nothing on a supported path reads them.
|
|
360
|
-
* @see lib/neatogen/neatoinit.c:makeGraphData
|
|
361
|
-
* @see lib/neatogen/stuff.c:setEdgeLen
|
|
362
|
-
*/
|
|
363
|
-
export function makeGraphDataC(
|
|
364
|
-
g: Graph, nodeList: Node[], haveLen: boolean, haveDir: boolean,
|
|
365
|
-
): VtxData[] {
|
|
366
|
-
const vtx: VtxData[] = nodeList.map((_, i) => ({
|
|
367
|
-
nedges: 1, edges: [i], ewgts: haveLen || haveDir ? [0] : [],
|
|
368
|
-
}));
|
|
369
|
-
for (const np of nodeList) {
|
|
370
|
-
const i = np.info.id!;
|
|
371
|
-
const entry = vtx[i]!;
|
|
372
|
-
const seen = new Map<number, number>();
|
|
373
|
-
for (const ep of [...np.outEdges(g), ...np.inEdges(g)]) {
|
|
374
|
-
if (ep.head === ep.tail) continue;
|
|
375
|
-
const vp = ep.tail === np ? ep.head : ep.tail;
|
|
376
|
-
const vid = vp.info.id!;
|
|
377
|
-
const dist = haveLen ? lateDouble(ep.attrs.get('len'), 1.0, 0) : 1.0;
|
|
378
|
-
const idx = seen.get(vid);
|
|
379
|
-
if (idx !== undefined) {
|
|
380
|
-
if (haveLen) entry.ewgts[idx] = Math.max(entry.ewgts[idx]!, dist);
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
seen.set(vid, entry.nedges);
|
|
384
|
-
entry.edges.push(vid);
|
|
385
|
-
if (haveLen || haveDir) entry.ewgts.push(dist); // C neatoinit.c:827: haveDir 1.0
|
|
386
|
-
entry.nedges++;
|
|
387
|
-
}
|
|
395
|
+
for (let i = 0; i < n; i++) {
|
|
396
|
+
nodeList[i]!.info.pos = [dCoords[0]![i]!, dCoords[1]![i]!];
|
|
388
397
|
}
|
|
389
|
-
return vtx;
|
|
390
398
|
}
|
|
391
399
|
|
|
392
|
-
/**
|
|
393
|
-
* Dispatch to the appropriate layout solver for the given mode.
|
|
394
|
-
*
|
|
395
|
-
* @see lib/neatogen/neatoinit.c:neatoLayout
|
|
396
|
-
*/
|
|
400
|
+
/** Dispatch to the layout solver for the mode. @see lib/neatogen/neatoinit.c:neatoLayout */
|
|
397
401
|
export function solveModel(g: Graph, mode: number, model: number): void {
|
|
398
402
|
if (g.nodes.size < 2) return;
|
|
399
403
|
assertSupported(g, mode, model);
|
|
@@ -411,11 +415,7 @@ export function solveModel(g: Graph, mode: number, model: number): void {
|
|
|
411
415
|
// neatoTranslate helpers
|
|
412
416
|
// ---------------------------------------------------------------------------
|
|
413
417
|
|
|
414
|
-
/**
|
|
415
|
-
* Find minimum X and Y across all node positions.
|
|
416
|
-
*
|
|
417
|
-
* @see lib/neatogen/neatoinit.c (translate logic)
|
|
418
|
-
*/
|
|
418
|
+
/** Minimum X and Y across all node positions. @see lib/neatogen/neatoinit.c (translate logic) */
|
|
419
419
|
export function findMinPos(g: Graph): { minX: number; minY: number } {
|
|
420
420
|
let minX = Infinity;
|
|
421
421
|
let minY = Infinity;
|
package/src/layout/neato/kk.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { MODEL_MDS } from './init.js';
|
|
|
27
27
|
import { solve } from './kk-solve.js';
|
|
28
28
|
import { lenattr } from './edge-len.js';
|
|
29
29
|
import { newPathState, shortestPath, mdsModel, type Neighbour } from './kk-paths.js';
|
|
30
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
30
31
|
|
|
31
32
|
/** `Ndim`: layout dimension (this port lays out in 2-D only). */
|
|
32
33
|
const NDIM = 2;
|
|
@@ -289,7 +290,7 @@ function moveNode(st: KkState, m: number): void {
|
|
|
289
290
|
function solveModel(g: Graph, st: KkState): void {
|
|
290
291
|
for (let m = chooseNode(st); m >= 0; m = chooseNode(st)) moveNode(st, m);
|
|
291
292
|
if (st.move === st.maxIter) {
|
|
292
|
-
|
|
293
|
+
agwarningf(`Max. iterations (${st.maxIter}) reached on graph ${g.name}`);
|
|
293
294
|
}
|
|
294
295
|
}
|
|
295
296
|
|