@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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helpers of neatogen/kkutils.c: the all-pairs shortest-path matrix
|
|
5
|
+
* (`compute_apsp`) and the index sorts `quicksort_place` and
|
|
6
|
+
* `quicksort_placef`. The sorts go through `gv_sort`, i.e. libc `qsort`, which is
|
|
7
|
+
* not stable: tied keys keep the permutation the BSD/Apple qsort gives, so the
|
|
8
|
+
* sort runs through `gvQsort` (the reproduction of that algorithm).
|
|
9
|
+
*
|
|
10
|
+
* @see lib/neatogen/kkutils.c
|
|
11
|
+
* @see lib/util/sort.h:gv_sort
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { gvQsort } from '../../util/bsd-qsort.js';
|
|
15
|
+
import { bfs } from './bfs.js';
|
|
16
|
+
import { ngdijkstra } from './dijkstra.js';
|
|
17
|
+
import type { VtxData } from './dijkstra.js';
|
|
18
|
+
|
|
19
|
+
/** Three-way compare of two keys; 0 on tie or NaN, as C's `<`/`>` chain. */
|
|
20
|
+
function compareKeys(a: number, b: number): number {
|
|
21
|
+
if (a < b) return -1;
|
|
22
|
+
if (a > b) return 1;
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sort `ordering[first..first+count)` with `qsort` by `place[ordering[i]]`.
|
|
28
|
+
* `gv_sort` skips the call when `nmemb <= 1`; `gvQsort` does the same.
|
|
29
|
+
* @see lib/util/sort.h:gv_sort
|
|
30
|
+
*/
|
|
31
|
+
function gvSortByPlace(
|
|
32
|
+
place: Float64Array | Float32Array,
|
|
33
|
+
ordering: Int32Array,
|
|
34
|
+
first: number,
|
|
35
|
+
count: number,
|
|
36
|
+
): void {
|
|
37
|
+
const slice = Array.from(ordering.subarray(first, first + count));
|
|
38
|
+
gvQsort(slice, (x, y) => compareKeys(place[x]!, place[y]!));
|
|
39
|
+
for (let i = 0; i < count; i++) ordering[first + i] = slice[i]!;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sort `ordering[0..size)` by ascending `place[ordering[i]]`.
|
|
44
|
+
* @see lib/neatogen/kkutils.c:quicksort_place
|
|
45
|
+
*/
|
|
46
|
+
export function quicksortPlace(place: Float64Array, ordering: Int32Array, size: number): void {
|
|
47
|
+
gvSortByPlace(place, ordering, 0, size);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sort `ordering[first..last]` (inclusive) by ascending `place[ordering[i]]`
|
|
52
|
+
* (float keys); a no-op unless `first < last`.
|
|
53
|
+
* @see lib/neatogen/kkutils.c:quicksort_placef
|
|
54
|
+
*/
|
|
55
|
+
export function quicksortPlacef(
|
|
56
|
+
place: Float32Array,
|
|
57
|
+
ordering: Int32Array,
|
|
58
|
+
first: number,
|
|
59
|
+
last: number,
|
|
60
|
+
): void {
|
|
61
|
+
if (first < last) {
|
|
62
|
+
gvSortByPlace(place, ordering, first, last - first + 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Weighted APSP: one `ngdijkstra` per source. `DistType` is C `int`.
|
|
68
|
+
* @see lib/neatogen/kkutils.c:compute_apsp_dijkstra
|
|
69
|
+
*/
|
|
70
|
+
function computeApspDijkstra(graph: VtxData[], n: number): Int32Array[] {
|
|
71
|
+
const dij: Int32Array[] = [];
|
|
72
|
+
for (let i = 0; i < n; i++) dij.push(new Int32Array(n));
|
|
73
|
+
for (let i = 0; i < n; i++) ngdijkstra(i, graph, n, dij[i]!);
|
|
74
|
+
return dij;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Unweighted APSP: one `bfs` per source.
|
|
79
|
+
* @see lib/neatogen/kkutils.c:compute_apsp_simple
|
|
80
|
+
*/
|
|
81
|
+
function computeApspSimple(graph: VtxData[], n: number): Int32Array[] {
|
|
82
|
+
const dij: Int32Array[] = [];
|
|
83
|
+
for (let i = 0; i < n; i++) dij.push(new Int32Array(n));
|
|
84
|
+
for (let i = 0; i < n; i++) bfs(i, graph, n, dij[i]!);
|
|
85
|
+
return dij;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* All-pairs shortest-path lengths as `DistType` (int) rows: Dijkstra when the
|
|
90
|
+
* graph has edge lengths (`graph->ewgts`; the port's empty array is C NULL),
|
|
91
|
+
* BFS otherwise.
|
|
92
|
+
* @see lib/neatogen/kkutils.c:compute_apsp
|
|
93
|
+
*/
|
|
94
|
+
export function computeApsp(graph: VtxData[], n: number): Int32Array[] {
|
|
95
|
+
return graph[0]!.ewgts.length > 0 ? computeApspDijkstra(graph, n) : computeApspSimple(graph, n);
|
|
96
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Neato's sparse graph (`vtx_data`) construction: `makeGraphData` and the
|
|
4
|
+
* DIGCOLA edge-direction pass (`dfsCycle`, `acyclic`) it ends with.
|
|
5
|
+
*
|
|
6
|
+
* @see lib/neatogen/neatoinit.c
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { lateDouble } from '../../common/nodeinit.js';
|
|
10
|
+
import type { Graph } from '../../model/graph.js';
|
|
11
|
+
import type { Node } from '../../model/node.js';
|
|
12
|
+
import type { DirectedVtxData } from './opt-arrangement.js';
|
|
13
|
+
|
|
14
|
+
/** `MODE_IPSEP`, restated: init.ts imports this module. @see lib/neatogen/neatoprocs.h */
|
|
15
|
+
const MODE_IPSEP = 3;
|
|
16
|
+
|
|
17
|
+
/** `edists` value of an edge seen from its head (an in-edge). */
|
|
18
|
+
const EDIST_IN = 1;
|
|
19
|
+
/** `edists` value of an edge seen from its tail (an out-edge). */
|
|
20
|
+
const EDIST_OUT = -1;
|
|
21
|
+
/** `edists` value of an edge with `dir=none`. */
|
|
22
|
+
const EDIST_NONE = 0;
|
|
23
|
+
|
|
24
|
+
/** Any edge carries a len attr (C: agattr_text(g, AGEDGE, "len")). */
|
|
25
|
+
export function graphHasLen(g: Graph): boolean {
|
|
26
|
+
for (const e of g.edges) {
|
|
27
|
+
if (e.attrs.has('len')) return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `edists` of a graph built with `haveDir` (always an array there; `NULL` otherwise). */
|
|
33
|
+
const edistsOf = (v: DirectedVtxData): number[] => v.edists as number[];
|
|
34
|
+
|
|
35
|
+
/** One `dfsCycle` activation: the vertex and the next `edges` slot to examine. */
|
|
36
|
+
interface DfsFrame {
|
|
37
|
+
readonly i: number;
|
|
38
|
+
e: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The visit state C keeps in `ND_mark` / `ND_onstack`. */
|
|
42
|
+
interface DfsState {
|
|
43
|
+
readonly mode: number;
|
|
44
|
+
readonly mark: Uint8Array;
|
|
45
|
+
readonly onstack: Uint8Array;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Back edge `i -> j` (slot `e` of `i`): reverse it. In IPSEP mode make it an
|
|
50
|
+
* in-edge at both ends, so that an edge constraint won't be generated!
|
|
51
|
+
* @see lib/neatogen/neatoinit.c:dfsCycle
|
|
52
|
+
*/
|
|
53
|
+
function reverseBackEdge(graph: DirectedVtxData[], i: number, e: number, mode: number): void {
|
|
54
|
+
const node = graph[i]!;
|
|
55
|
+
const j = node.edges[e]!;
|
|
56
|
+
edistsOf(node)[e] = mode === MODE_IPSEP ? EDIST_OUT : EDIST_IN;
|
|
57
|
+
const target = graph[j]!;
|
|
58
|
+
let f = 1;
|
|
59
|
+
while (f < target.nedges && target.edges[f] !== i) f++;
|
|
60
|
+
if (f >= target.nedges) throw new Error(`neato: edge ${i}-${j} has no reverse entry`);
|
|
61
|
+
edistsOf(target)[f] = EDIST_OUT;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* dfs for breaking cycles in vtxdata. C recurses; the recursion is replaced by
|
|
66
|
+
* an explicit stack of (vertex, next edge slot) frames, which visits in the
|
|
67
|
+
* same order but cannot overflow the JS stack on a long path.
|
|
68
|
+
* @see lib/neatogen/neatoinit.c:dfsCycle
|
|
69
|
+
*/
|
|
70
|
+
export function dfsCycle(graph: DirectedVtxData[], start: number, st: DfsState): void {
|
|
71
|
+
const { mode, mark, onstack } = st;
|
|
72
|
+
const stack: DfsFrame[] = [{ i: start, e: 1 }];
|
|
73
|
+
mark[start] = 1;
|
|
74
|
+
onstack[start] = 1;
|
|
75
|
+
for (let frame = stack.at(-1); frame !== undefined; frame = stack.at(-1)) {
|
|
76
|
+
const { i } = frame;
|
|
77
|
+
const node = graph[i]!;
|
|
78
|
+
if (frame.e >= node.nedges) {
|
|
79
|
+
onstack[i] = 0;
|
|
80
|
+
stack.pop();
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const e = frame.e++;
|
|
84
|
+
if (edistsOf(node)[e] === EDIST_IN) continue; // in edge
|
|
85
|
+
const j = node.edges[e]!;
|
|
86
|
+
if (onstack[j]) {
|
|
87
|
+
reverseBackEdge(graph, i, e, mode);
|
|
88
|
+
} else if (!mark[j]) {
|
|
89
|
+
mark[j] = 1;
|
|
90
|
+
onstack[j] = 1;
|
|
91
|
+
stack.push({ i: j, e: 1 });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* do a dfs of the vtx_data, looking for cycles, reversing edges. C keeps the
|
|
98
|
+
* visit state in `ND_mark`/`ND_onstack`; it is local to this pass here.
|
|
99
|
+
* @see lib/neatogen/neatoinit.c:acyclic
|
|
100
|
+
*/
|
|
101
|
+
export function acyclic(graph: DirectedVtxData[], nv: number, mode: number): void {
|
|
102
|
+
const st: DfsState = { mode, mark: new Uint8Array(nv), onstack: new Uint8Array(nv) };
|
|
103
|
+
for (let i = 0; i < nv; i++) {
|
|
104
|
+
if (st.mark[i]) continue;
|
|
105
|
+
dfsCycle(graph, i, st);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** `edists` of the edge `ep` as seen from `np`. @see lib/neatogen/neatoinit.c:makeGraphData */
|
|
110
|
+
function edgeDist(dir: string | undefined, np: Node, head: Node): number {
|
|
111
|
+
if (dir !== undefined && dir.startsWith('none')) return EDIST_NONE;
|
|
112
|
+
return np === head ? EDIST_IN : EDIST_OUT;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** What `makeGraphData` was asked to build. */
|
|
116
|
+
interface GraphDataOpts {
|
|
117
|
+
readonly haveLen: boolean;
|
|
118
|
+
readonly haveDir: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Append `np`'s neighbours (out-edges then in-edges, loops skipped, multi-edges
|
|
123
|
+
* merged) to its entry.
|
|
124
|
+
* @see lib/neatogen/neatoinit.c:makeGraphData
|
|
125
|
+
*/
|
|
126
|
+
function addNeighbours(g: Graph, np: Node, entry: DirectedVtxData, o: GraphDataOpts): void {
|
|
127
|
+
const seen = new Map<number, number>();
|
|
128
|
+
for (const ep of [...np.outEdges(g), ...np.inEdges(g)]) {
|
|
129
|
+
if (ep.head === ep.tail) continue;
|
|
130
|
+
const vp = ep.tail === np ? ep.head : ep.tail;
|
|
131
|
+
const vid = vp.info.id!;
|
|
132
|
+
const dist = o.haveLen ? lateDouble(ep.attrs.get('len'), 1.0, 0) : 1.0;
|
|
133
|
+
const idx = seen.get(vid);
|
|
134
|
+
if (idx !== undefined) {
|
|
135
|
+
if (o.haveLen) entry.ewgts[idx] = Math.max(entry.ewgts[idx]!, dist);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
seen.set(vid, entry.nedges);
|
|
139
|
+
entry.edges.push(vid);
|
|
140
|
+
if (o.haveLen || o.haveDir) entry.ewgts.push(dist); // C neatoinit.c:827: haveDir 1.0
|
|
141
|
+
if (o.haveDir) edistsOf(entry).push(edgeDist(ep.attrs.get('dir'), np, ep.head));
|
|
142
|
+
entry.nedges++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* C makeGraphData's vtx_data: per node out-edges then in-edges (agfstedge order), self at
|
|
148
|
+
* index 0, duplicate neighbours merged (len keeps the max). ewgts: lens (default 1.0) if any
|
|
149
|
+
* len attr, else 1.0 under haveDir (hier/ipsep), else absent (C NULL). Under haveDir `edists`
|
|
150
|
+
* holds the direction of each first-seen edge (-1 tail, +1 head, 0 for dir=none) and the
|
|
151
|
+
* graph is then made acyclic. eweights (haveWt) are not built: nothing on a supported path
|
|
152
|
+
* reads them.
|
|
153
|
+
* @see lib/neatogen/neatoinit.c:makeGraphData
|
|
154
|
+
* @see lib/neatogen/stuff.c:setEdgeLen
|
|
155
|
+
*/
|
|
156
|
+
export function makeGraphDataC(
|
|
157
|
+
g: Graph, nodeList: Node[], haveLen: boolean, haveDir: boolean, mode: number = -1,
|
|
158
|
+
): DirectedVtxData[] {
|
|
159
|
+
const vtx: DirectedVtxData[] = nodeList.map((_, i) => ({
|
|
160
|
+
nedges: 1, edges: [i], ewgts: haveLen || haveDir ? [0] : [],
|
|
161
|
+
...(haveDir ? { edists: [0] } : {}),
|
|
162
|
+
}));
|
|
163
|
+
for (const np of nodeList) addNeighbours(g, np, vtx[np.info.id!]!, { haveLen, haveDir });
|
|
164
|
+
if (haveDir) acyclic(vtx, nodeList.length, mode); // make graph acyclic
|
|
165
|
+
return vtx;
|
|
166
|
+
}
|
|
@@ -10,8 +10,22 @@
|
|
|
10
10
|
* @see lib/neatogen/matrix_ops.c
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { crand } from '../../common/crand.js';
|
|
14
|
+
import { fma as softFma } from '../../common/fma.js';
|
|
15
|
+
import type { VtxData } from './dijkstra.js';
|
|
16
|
+
|
|
13
17
|
const fr = Math.fround;
|
|
14
18
|
|
|
19
|
+
/**
|
|
20
|
+
* fmadd: round(a * b + c) once. `common/fma` splits operands (Dekker), which
|
|
21
|
+
* turns an infinite operand into NaN where the hardware fmadd gives inf, so
|
|
22
|
+
* non-finite products and addends take the plain expression (exact then).
|
|
23
|
+
*/
|
|
24
|
+
export function fma(a: number, b: number, c: number): number {
|
|
25
|
+
const p = a * b;
|
|
26
|
+
return Number.isFinite(p) && Number.isFinite(c) ? softFma(a, b, c) : p + c;
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
/** Subtract the mean (double precision). @see matrix_ops.c:orthog1 */
|
|
16
30
|
export function orthog1(n: number, vec: Float64Array): void {
|
|
17
31
|
let sum = 0;
|
|
@@ -110,3 +124,127 @@ export function invertSqrtVec(n: number, vec: Float32Array): void {
|
|
|
110
124
|
if (vec[i]! > 0) vec[i] = 1 / fr(Math.sqrt(vec[i]!));
|
|
111
125
|
}
|
|
112
126
|
}
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Double-precision helpers (vectors_inner_product, scadd, norm, ...).
|
|
130
|
+
//
|
|
131
|
+
// The 16.1 oracle is built `-O3 -flto=thin` for arm64, so each helper is
|
|
132
|
+
// inlined into every call site and compiled there on its own: a vectorized
|
|
133
|
+
// reduction rounds the products of its leading `block * floor(n / block)`
|
|
134
|
+
// elements before the in-order adds (no reassociation is allowed), while its
|
|
135
|
+
// scalar remainder (or a whole scalar loop) contracts `acc += a * b` to fmadd.
|
|
136
|
+
// The block width is therefore a property of the call site; callers pass it,
|
|
137
|
+
// named after the site, and `BLOCK_NONE` stands for a plain scalar loop.
|
|
138
|
+
// Elementwise `x += a * b` (scadd) is fmadd/fmla in every shape.
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
/** Block width of a call site compiled as a scalar loop (all fmadd). */
|
|
142
|
+
export const BLOCK_NONE = 1;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Sum of `a[i] * b[i]` over `n` terms, the first `block * floor(n / block)`
|
|
146
|
+
* unfused and the rest fused, as the oracle's inlined copy of the site.
|
|
147
|
+
* @see lib/neatogen/matrix_ops.c:vectors_inner_product
|
|
148
|
+
*/
|
|
149
|
+
export function innerProduct(n: number, v1: Float64Array, v2: Float64Array, block: number): number {
|
|
150
|
+
let result = 0;
|
|
151
|
+
const blocked = block === BLOCK_NONE ? 0 : n - (n % block);
|
|
152
|
+
let i = 0;
|
|
153
|
+
for (; i < blocked; i++) result += v1[i]! * v2[i]!;
|
|
154
|
+
for (; i < n; i++) result = fma(v1[i]!, v2[i]!, result);
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** vec1[0..end] += fac * vec2[0..end] (fmadd). @see lib/neatogen/matrix_ops.c:scadd */
|
|
159
|
+
export function scadd(vec1: Float64Array, end: number, fac: number, vec2: Float64Array): void {
|
|
160
|
+
for (let i = 0; i <= end; i++) vec1[i] = fma(fac, vec2[i]!, vec1[i]!);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** 2-norm of vec[0..end]. @see lib/neatogen/matrix_ops.c:norm */
|
|
164
|
+
export function norm(vec: Float64Array, end: number, block: number): number {
|
|
165
|
+
return Math.sqrt(innerProduct(end + 1, vec, vec, block));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** result = vector * alpha (aliasing allowed). @see lib/neatogen/matrix_ops.c:vectors_scalar_mult */
|
|
169
|
+
export function vectorsScalarMult(
|
|
170
|
+
n: number, vector: Float64Array, alpha: number, result: Float64Array,
|
|
171
|
+
): void {
|
|
172
|
+
for (let i = 0; i < n; i++) result[i] = vector[i]! * alpha;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** dest = source. @see lib/neatogen/matrix_ops.c:copy_vector */
|
|
176
|
+
export function copyVector(n: number, source: Float64Array, dest: Float64Array): void {
|
|
177
|
+
for (let i = 0; i < n; i++) dest[i] = source[i]!;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** result = v1 + v2 (aliasing allowed). @see lib/neatogen/matrix_ops.c:vectors_addition */
|
|
181
|
+
export function vectorsAddition(
|
|
182
|
+
n: number, v1: Float64Array, v2: Float64Array, result: Float64Array,
|
|
183
|
+
): void {
|
|
184
|
+
for (let i = 0; i < n; i++) result[i] = v1[i]! + v2[i]!;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** result = v1 - v2 (aliasing allowed). @see lib/neatogen/matrix_ops.c:vectors_subtraction */
|
|
188
|
+
export function vectorsSubtraction(
|
|
189
|
+
n: number, v1: Float64Array, v2: Float64Array, result: Float64Array,
|
|
190
|
+
): void {
|
|
191
|
+
for (let i = 0; i < n; i++) result[i] = v1[i]! - v2[i]!;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Largest |vector[i]| (start -1e50). @see lib/neatogen/matrix_ops.c:max_abs */
|
|
195
|
+
export function maxAbs(n: number, vector: Float64Array): number {
|
|
196
|
+
let maxVal = -1e50;
|
|
197
|
+
for (let i = 0; i < n; i++) {
|
|
198
|
+
const a = Math.abs(vector[i]!);
|
|
199
|
+
if (a > maxVal) maxVal = a; // fmax: a NaN operand is ignored, as `>` does
|
|
200
|
+
}
|
|
201
|
+
return maxVal;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* result = matrix * vector for an n x n float** matrix and double vector;
|
|
206
|
+
* each row's reduction is blocked as the call site was compiled.
|
|
207
|
+
* @see lib/neatogen/matrix_ops.c:right_mult_with_vector_f
|
|
208
|
+
*/
|
|
209
|
+
export function rightMultWithVectorF(
|
|
210
|
+
matrix: readonly Float32Array[], n: number, vector: Float64Array, result: Float64Array,
|
|
211
|
+
block: number,
|
|
212
|
+
): void {
|
|
213
|
+
const blocked = block === BLOCK_NONE ? 0 : n - (n % block);
|
|
214
|
+
for (let i = 0; i < n; i++) {
|
|
215
|
+
const row = matrix[i]!;
|
|
216
|
+
let res = 0;
|
|
217
|
+
let j = 0;
|
|
218
|
+
for (; j < blocked; j++) res += row[j]! * vector[j]!;
|
|
219
|
+
for (; j < n; j++) res = fma(row[j]!, vector[j]!, res);
|
|
220
|
+
result[i] = res;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* result = matrix * vector over the sparse `vtx_data` rows (`ewgts` weights).
|
|
226
|
+
* The loop is scalar in the oracle: every term is an fmadd.
|
|
227
|
+
* @see lib/neatogen/matrix_ops.c:right_mult_with_vector
|
|
228
|
+
*/
|
|
229
|
+
export function rightMultWithVector(
|
|
230
|
+
matrix: VtxData[], n: number, vector: Float64Array, result: Float64Array,
|
|
231
|
+
): void {
|
|
232
|
+
for (let i = 0; i < n; i++) {
|
|
233
|
+
let res = 0;
|
|
234
|
+
const row = matrix[i]!;
|
|
235
|
+
for (let j = 0; j < row.nedges; j++) res = fma(row.ewgts[j]!, vector[row.edges[j]!]!, res);
|
|
236
|
+
result[i] = res;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** `#define RANGE` @see lib/neatogen/matrix_ops.c */
|
|
241
|
+
const RANGE = 500;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* `vec[i] = rand() % RANGE`, then orthogonalize against 1.
|
|
245
|
+
* @see lib/neatogen/matrix_ops.c:init_vec_orth1
|
|
246
|
+
*/
|
|
247
|
+
export function initVecOrth1(n: number, vec: Float64Array): void {
|
|
248
|
+
for (let i = 0; i < n; i++) vec[i] = crand() % RANGE;
|
|
249
|
+
orthog1(n, vec);
|
|
250
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { Point, Poly } from '../../pathplan/types.js';
|
|
20
20
|
import { wind } from '../../pathplan/visibility.js';
|
|
21
21
|
import { mkSurface } from './cdt-surface.js';
|
|
22
|
+
import { dist2 } from '../../common/spline-midpoint.js';
|
|
22
23
|
import { InternalError } from '../../errors.js';
|
|
23
24
|
|
|
24
25
|
/** index pair. @see multispline.c:ipair */
|
|
@@ -87,9 +88,8 @@ function sharedEdge(faces: number[], p3: number, q3: number): Ipair {
|
|
|
87
88
|
|
|
88
89
|
/** @see multispline.c:addTriEdge */
|
|
89
90
|
export function addTriEdge(g: Tgraph, t: number, h: number, seg: Ipair): void {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
g.nodes[t]!.ctr.y - g.nodes[h]!.ctr.y);
|
|
91
|
+
/* DIST = sqrt(fused DIST2): libgvplugin_neato_layout _addTriEdge fmul/fmadd/fsqrt */
|
|
92
|
+
const dist = Math.sqrt(dist2(g.nodes[t]!.ctr, g.nodes[h]!.ctr));
|
|
93
93
|
const idx = g.edges.length;
|
|
94
94
|
g.edges.push({ t, h, seg, dist });
|
|
95
95
|
g.nodes[t]!.edges.push(idx);
|
|
@@ -25,6 +25,7 @@ import { clipAndInstall } from '../../common/splines-clip.js';
|
|
|
25
25
|
import { makeStraightEdges, addEdgeLabels } from '../dot/straight-edges.js';
|
|
26
26
|
import type { Router, Ipair } from './multispline-router.js';
|
|
27
27
|
import { addEndpoint, edgeToSeg, triPath } from './multispline-router.js';
|
|
28
|
+
import { agwarningf } from '../../common/agerr.js';
|
|
28
29
|
|
|
29
30
|
export type { Router } from './multispline-router.js';
|
|
30
31
|
export { mkRouter } from './multispline-router.js';
|
|
@@ -339,8 +340,8 @@ function chainOf(e: Edge): Edge[] {
|
|
|
339
340
|
* @see multispline.c:genroute
|
|
340
341
|
*/
|
|
341
342
|
function warnNoControlPoints(e: Edge): number {
|
|
342
|
-
|
|
343
|
-
`
|
|
343
|
+
agwarningf(
|
|
344
|
+
`Could not create control points for multiple spline for edge (${e.tail.name},${e.head.name})`,
|
|
344
345
|
);
|
|
345
346
|
return 1;
|
|
346
347
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Optimal y arrangement of a directed graph: solves `L*y = b` by conjugate
|
|
5
|
+
* gradient over the unit-weight Laplacian, where `b` is the balance vector of
|
|
6
|
+
* the edge directions (`edists`).
|
|
7
|
+
*
|
|
8
|
+
* @see lib/neatogen/opt_arrangement.c
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { conjugateGradient } from './conjgrad.js';
|
|
12
|
+
import type { VtxData } from './dijkstra.js';
|
|
13
|
+
import { initVecOrth1 } from './matrix-ops.js';
|
|
14
|
+
|
|
15
|
+
/** `vtx_data` with the `DIGCOLA` `edists` field (`int8_t *`, `NULL` when absent). */
|
|
16
|
+
export interface DirectedVtxData extends VtxData {
|
|
17
|
+
edists?: ArrayLike<number> | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** `#define hierarchy_cg_tol` @see lib/neatogen/opt_arrangement.c */
|
|
21
|
+
const HIERARCHY_CG_TOL = 1e-3;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build `b` with `-b[i] = sum_j -w_ij * delta_ij` (the balance vector; the
|
|
25
|
+
* matrix is -L, so `-Lx = -b` is solved). `b` is left untouched when node 0 has
|
|
26
|
+
* no `edists` (C: `continue`, `b` stays calloc-zero).
|
|
27
|
+
* @see lib/neatogen/opt_arrangement.c:construct_b
|
|
28
|
+
*/
|
|
29
|
+
export function constructB(graph: DirectedVtxData[], n: number, b: Float64Array): void {
|
|
30
|
+
for (let i = 0; i < n; i++) {
|
|
31
|
+
if (graph[0]!.edists == null) continue;
|
|
32
|
+
let bI = 0;
|
|
33
|
+
const node = graph[i]!;
|
|
34
|
+
for (let j = 1; j < node.nedges; j++) { // skip the self loop
|
|
35
|
+
// C ewgts is float; edists is int8_t
|
|
36
|
+
bI += Math.fround(Math.fround(node.ewgts[j]!) * node.edists![j]!);
|
|
37
|
+
}
|
|
38
|
+
b[i] = bI;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Replace every row's edge weights (lengths) by uniform Laplacian weights:
|
|
44
|
+
* `-(nedges-1)` on the diagonal, 1 off it. Returns the replaced rows' weights.
|
|
45
|
+
* @see lib/neatogen/opt_arrangement.c:compute_y_coords
|
|
46
|
+
*/
|
|
47
|
+
function setUniformWeights(graph: VtxData[], n: number): number[][] {
|
|
48
|
+
const old: number[][] = [];
|
|
49
|
+
for (let i = 0; i < n; i++) {
|
|
50
|
+
const node = graph[i]!;
|
|
51
|
+
old.push(node.ewgts);
|
|
52
|
+
const weights = new Array<number>(node.nedges).fill(1);
|
|
53
|
+
weights[0] = -(node.nedges - 1);
|
|
54
|
+
node.ewgts = weights;
|
|
55
|
+
}
|
|
56
|
+
return old;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find the y coordinates of a directed graph by solving `L*y = b`. Starts from
|
|
61
|
+
* a `rand()`-seeded vector, so the result depends on the libc rand stream
|
|
62
|
+
* position (`common/crand`). Returns 1 when conjugate gradient fails.
|
|
63
|
+
* @see lib/neatogen/opt_arrangement.c:compute_y_coords
|
|
64
|
+
*/
|
|
65
|
+
export function computeYCoords(
|
|
66
|
+
graph: VtxData[],
|
|
67
|
+
n: number,
|
|
68
|
+
yCoords: Float64Array,
|
|
69
|
+
maxIterations: number,
|
|
70
|
+
): 0 | 1 {
|
|
71
|
+
let rv: 0 | 1 = 0;
|
|
72
|
+
const b = new Float64Array(n);
|
|
73
|
+
|
|
74
|
+
constructB(graph, n, b);
|
|
75
|
+
initVecOrth1(n, yCoords);
|
|
76
|
+
|
|
77
|
+
// replace original edge weights (which are lengths) with uniform weights
|
|
78
|
+
const oldEwgts = setUniformWeights(graph, n);
|
|
79
|
+
// C tests `< 0` although conjugate_gradient returns 0 or 1, so rv never becomes 1
|
|
80
|
+
if (conjugateGradient(graph, yCoords, b, n, HIERARCHY_CG_TOL, maxIterations) < 0) {
|
|
81
|
+
rv = 1;
|
|
82
|
+
}
|
|
83
|
+
// restore original edge weights
|
|
84
|
+
for (let i = 0; i < n; i++) graph[i]!.ewgts = oldEwgts[i]!;
|
|
85
|
+
return rv;
|
|
86
|
+
}
|
package/src/layout/neato/poly.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type { Node } from '../../model/node.js';
|
|
|
19
19
|
import type { Box, Point } from '../../model/geom.js';
|
|
20
20
|
import { ShapeKind } from '../../common/types.js';
|
|
21
21
|
import type { PolygonT } from '../../common/types.js';
|
|
22
|
+
import { agerrorf } from '../../common/agerr.js';
|
|
22
23
|
|
|
23
24
|
/** @see lib/neatogen/poly.c:BOX */
|
|
24
25
|
export const POLY_BOX = 1;
|
|
@@ -258,7 +259,7 @@ export function makeAddPoly(n: Node, xmargin: number, ymargin: number): Poly | n
|
|
|
258
259
|
case ShapeKind.SH_POINT:
|
|
259
260
|
return finishPoly(genRound(n, xmargin, ymargin), POLY_CIRCLE);
|
|
260
261
|
default:
|
|
261
|
-
|
|
262
|
+
agerrorf(`makeAddPoly: unknown shape type ${shapeName(n)}\n`);
|
|
262
263
|
return null;
|
|
263
264
|
}
|
|
264
265
|
}
|
|
@@ -296,7 +297,7 @@ function baseShape(n: Node): { verts: Point[]; kind: number } | null {
|
|
|
296
297
|
case ShapeKind.SH_POINT:
|
|
297
298
|
return { verts: genRound(n, 0, 0), kind: POLY_CIRCLE };
|
|
298
299
|
default:
|
|
299
|
-
|
|
300
|
+
agerrorf(`makePoly: unknown shape type ${shapeName(n)}\n`);
|
|
300
301
|
return null;
|
|
301
302
|
}
|
|
302
303
|
}
|