@knowvah/dot-engine 2.6.12 → 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/common/htmltable-emit-fill.d.ts +20 -2
- package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
- package/dist/common/htmltable-emit.d.ts.map +1 -1
- 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.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/splines-clip.d.ts +7 -0
- package/dist/common/splines-clip.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.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.js +2460 -901
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/compound.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-clip.d.ts +14 -4
- 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/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/gts-triangle.d.ts +14 -0
- package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
- package/dist/layout/neato/init.d.ts +2 -12
- package/dist/layout/neato/init.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/opt-arrangement.d.ts +20 -0
- package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
- 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/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/render/dot/edge-draw.d.ts +1 -1
- package/dist/render/dot/edge-draw.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.js +2340 -781
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/common/graph-init.ts +1 -1
- package/src/common/htmltable-emit-fill.ts +56 -11
- package/src/common/htmltable-emit.ts +4 -3
- package/src/common/htmltable-lex-expat.ts +21 -3
- package/src/common/htmltable-pos-runs.ts +11 -6
- package/src/common/htmltable.ts +28 -116
- package/src/common/libm-trig.ts +43 -0
- package/src/common/multicolor.ts +88 -24
- package/src/common/poly-gencode.ts +16 -8
- package/src/common/poly-init.ts +19 -1
- package/src/common/postproc.ts +2 -2
- package/src/common/splines-clip.ts +15 -12
- package/src/common/splines-routespl.ts +2 -2
- package/src/common/textmeasure-lookup.ts +1 -1
- package/src/gvc/context.ts +8 -0
- package/src/gvc/device-cluster.ts +63 -8
- package/src/gvc/device.ts +16 -2
- 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/layout/dot/cluster.ts +1 -1
- package/src/layout/dot/compound.ts +3 -4
- package/src/layout/dot/conc.ts +1 -1
- package/src/layout/dot/edge-route-clip.ts +19 -7
- package/src/layout/dot/edge-route-routing.ts +11 -3
- package/src/layout/dot/fastgr.ts +2 -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 +6 -16
- 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 -2
- package/src/layout/neato/fdp-adjust.ts +3 -3
- package/src/layout/neato/gts-triangle.ts +42 -0
- package/src/layout/neato/init.ts +57 -57
- package/src/layout/neato/kk.ts +2 -2
- 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/opt-arrangement.ts +86 -0
- package/src/layout/neato/quad-prog-solve.ts +489 -0
- package/src/layout/neato/smart-ini-x.ts +503 -0
- package/src/layout/neato/splines.ts +4 -4
- package/src/layout/neato/start.ts +7 -6
- 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 -3
- package/src/parser/dot.js +1 -1
- package/src/parser/dot.pegjs +1 -1
- package/src/parser/index.ts +1 -1
- package/src/render/color-resolve.ts +2 -2
- package/src/render/dot/edge-draw.ts +6 -3
- package/src/render/dot.ts +1 -1
- package/src/render/svg-edge-split.ts +2 -2
- package/src/render/svg-multicolor.ts +4 -3
- package/src/render/svg-parallel-edge.ts +2 -2
|
@@ -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,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { Graph } from '../../model/graph.js';
|
|
11
11
|
import type { Edge } from '../../model/edge.js';
|
|
12
|
-
import {
|
|
12
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
13
13
|
|
|
14
14
|
/** `dfltlen` passed by scan_graph_mode. @see lib/neatogen/stuff.c:scan_graph_mode */
|
|
15
15
|
export const DEFAULT_EDGE_LEN = 1.0;
|
|
@@ -25,7 +25,7 @@ export function lenattr(ep: Edge, g: Graph): number {
|
|
|
25
25
|
const val = /^\s*[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/.exec(s);
|
|
26
26
|
const len = val === null ? Number.NaN : parseFloat(val[0]);
|
|
27
27
|
if (Number.isNaN(len) || len < 0 || len === 0) {
|
|
28
|
-
|
|
28
|
+
agwarningf(`bad edge len "${s}" in ${g.name} - setting to ${DEFAULT_EDGE_LEN.toFixed(2)}`);
|
|
29
29
|
return DEFAULT_EDGE_LEN;
|
|
30
30
|
}
|
|
31
31
|
return len;
|
|
@@ -26,7 +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 {
|
|
29
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
30
30
|
|
|
31
31
|
/** @see lib/neatogen/adjust.c:DFLT_MARGIN (points) */
|
|
32
32
|
const DFLT_MARGIN = 4;
|
|
@@ -162,7 +162,7 @@ export function warnUnrecognizedOverlap(flag: string | undefined): void {
|
|
|
162
162
|
const s = flag.toLowerCase();
|
|
163
163
|
if (s.startsWith('prism') || NAMED_MODES.has(s)) return;
|
|
164
164
|
if (mapBoolDflt(flag, false) === mapBoolDflt(flag, true)) return;
|
|
165
|
-
|
|
165
|
+
agwarningf(`Unrecognized overlap value "${flag}" - using false`);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
@@ -204,7 +204,7 @@ function adjustLate(g: Graph, am: AdjustData): number {
|
|
|
204
204
|
case AM_IPSEP: return 0;
|
|
205
205
|
case AM_VPSC: return vpscAdjust(g);
|
|
206
206
|
default:
|
|
207
|
-
|
|
207
|
+
agwarningf(`Unhandled adjust option ${am.print}`);
|
|
208
208
|
return 0;
|
|
209
209
|
}
|
|
210
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,
|
|
@@ -329,72 +336,65 @@ export function neatoMaxIter(maxiter: string | undefined, mode: number, n: numbe
|
|
|
329
336
|
return mode === MODE_SGD ? SGD_ITERATIONS : 100 * n;
|
|
330
337
|
}
|
|
331
338
|
|
|
332
|
-
/**
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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;
|
|
336
363
|
const n = nodeList.length;
|
|
337
|
-
const haveLen = graphHasLen(g);
|
|
338
|
-
const vtx = makeGraphDataC(g, nodeList, haveLen, mode === MODE_HIER || mode === MODE_IPSEP);
|
|
339
|
-
checkStart(g); // C: srand48(seed) immediately before initLayout draws
|
|
340
364
|
const maxi = neatoMaxIter(g.attrs.get('maxiter'), mode, n);
|
|
341
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
|
+
}
|
|
342
371
|
// C ipsep (stress_majorization_cola, constrained_majorization_ipsep.c:150) always uses
|
|
343
372
|
// compute_apsp_packed; kD_mkernel uses the weighted APSP iff graph->ewgts (stress.c:855).
|
|
344
373
|
const weighted = haveLen && mode !== MODE_IPSEP;
|
|
345
374
|
const Dij = weighted ? computeWeightedApspPacked(vtx, n) : computeApspPacked(vtx, n);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
dim: DFLT_DIM, opts: OPT_EXP_FLAG, maxi, epsilon,
|
|
375
|
+
return stressMajorizationKDMkernel(Dij, n, dCoords, nodeList, {
|
|
376
|
+
dim: DFLT_DIM, opts, maxi, epsilon,
|
|
349
377
|
});
|
|
350
|
-
for (let i = 0; i < n; i++) {
|
|
351
|
-
nodeList[i]!.info.pos = [dCoords[0]![i]!, dCoords[1]![i]!];
|
|
352
|
-
}
|
|
353
378
|
}
|
|
354
379
|
|
|
355
|
-
/**
|
|
356
|
-
export function
|
|
357
|
-
|
|
358
|
-
|
|
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;
|
|
359
394
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* C makeGraphData's vtx_data: per node out-edges then in-edges (agfstedge order), self at
|
|
365
|
-
* index 0, duplicate neighbours merged (len keeps the max). ewgts: lens (default 1.0) if any
|
|
366
|
-
* len attr, else 1.0 under haveDir (hier/ipsep), else absent (C NULL). eweights (haveWt)
|
|
367
|
-
* and edists are not built: nothing on a supported path reads them.
|
|
368
|
-
* @see lib/neatogen/neatoinit.c:makeGraphData
|
|
369
|
-
* @see lib/neatogen/stuff.c:setEdgeLen
|
|
370
|
-
*/
|
|
371
|
-
export function makeGraphDataC(
|
|
372
|
-
g: Graph, nodeList: Node[], haveLen: boolean, haveDir: boolean,
|
|
373
|
-
): VtxData[] {
|
|
374
|
-
const vtx: VtxData[] = nodeList.map((_, i) => ({
|
|
375
|
-
nedges: 1, edges: [i], ewgts: haveLen || haveDir ? [0] : [],
|
|
376
|
-
}));
|
|
377
|
-
for (const np of nodeList) {
|
|
378
|
-
const i = np.info.id!;
|
|
379
|
-
const entry = vtx[i]!;
|
|
380
|
-
const seen = new Map<number, number>();
|
|
381
|
-
for (const ep of [...np.outEdges(g), ...np.inEdges(g)]) {
|
|
382
|
-
if (ep.head === ep.tail) continue;
|
|
383
|
-
const vp = ep.tail === np ? ep.head : ep.tail;
|
|
384
|
-
const vid = vp.info.id!;
|
|
385
|
-
const dist = haveLen ? lateDouble(ep.attrs.get('len'), 1.0, 0) : 1.0;
|
|
386
|
-
const idx = seen.get(vid);
|
|
387
|
-
if (idx !== undefined) {
|
|
388
|
-
if (haveLen) entry.ewgts[idx] = Math.max(entry.ewgts[idx]!, dist);
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
seen.set(vid, entry.nedges);
|
|
392
|
-
entry.edges.push(vid);
|
|
393
|
-
if (haveLen || haveDir) entry.ewgts.push(dist); // C neatoinit.c:827: haveDir 1.0
|
|
394
|
-
entry.nedges++;
|
|
395
|
-
}
|
|
395
|
+
for (let i = 0; i < n; i++) {
|
|
396
|
+
nodeList[i]!.info.pos = [dCoords[0]![i]!, dCoords[1]![i]!];
|
|
396
397
|
}
|
|
397
|
-
return vtx;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
/** Dispatch to the layout solver for the mode. @see lib/neatogen/neatoinit.c:neatoLayout */
|
package/src/layout/neato/kk.ts
CHANGED
|
@@ -27,7 +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 {
|
|
30
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
31
31
|
|
|
32
32
|
/** `Ndim`: layout dimension (this port lays out in 2-D only). */
|
|
33
33
|
const NDIM = 2;
|
|
@@ -290,7 +290,7 @@ function moveNode(st: KkState, m: number): void {
|
|
|
290
290
|
function solveModel(g: Graph, st: KkState): void {
|
|
291
291
|
for (let m = chooseNode(st); m >= 0; m = chooseNode(st)) moveNode(st, m);
|
|
292
292
|
if (st.move === st.maxIter) {
|
|
293
|
-
|
|
293
|
+
agwarningf(`Max. iterations (${st.maxIter}) reached on graph ${g.name}`);
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|