@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.
Files changed (270) hide show
  1. package/README.md +1 -1
  2. package/dist/api.js +18 -4
  3. package/dist/api.js.map +3 -3
  4. package/dist/async/render-async.d.ts.map +1 -1
  5. package/dist/common/agerr.d.ts +37 -0
  6. package/dist/common/agerr.d.ts.map +1 -0
  7. package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
  8. package/dist/common/color.d.ts.map +1 -1
  9. package/dist/common/crand.d.ts +0 -20
  10. package/dist/common/crand.d.ts.map +1 -1
  11. package/dist/common/emit-once.d.ts +6 -0
  12. package/dist/common/emit-once.d.ts.map +1 -0
  13. package/dist/common/graph-init.d.ts.map +1 -1
  14. package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
  15. package/dist/common/htmltable-emit-fill.d.ts +29 -2
  16. package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
  17. package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
  18. package/dist/common/htmltable-emit.d.ts.map +1 -1
  19. package/dist/common/htmltable-lex-expat.d.ts +1 -6
  20. package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
  21. package/dist/common/htmltable-pos-runs.d.ts +7 -4
  22. package/dist/common/htmltable-pos-runs.d.ts.map +1 -1
  23. package/dist/common/htmltable.d.ts +1 -7
  24. package/dist/common/htmltable.d.ts.map +1 -1
  25. package/dist/common/libm-trig.d.ts +5 -0
  26. package/dist/common/libm-trig.d.ts.map +1 -0
  27. package/dist/common/multicolor.d.ts +21 -13
  28. package/dist/common/multicolor.d.ts.map +1 -1
  29. package/dist/common/poly-gencode.d.ts +1 -2
  30. package/dist/common/poly-gencode.d.ts.map +1 -1
  31. package/dist/common/poly-init.d.ts.map +1 -1
  32. package/dist/common/postproc.d.ts.map +1 -1
  33. package/dist/common/random.d.ts.map +1 -1
  34. package/dist/common/render-state.d.ts +8 -0
  35. package/dist/common/render-state.d.ts.map +1 -0
  36. package/dist/common/splines-clip.d.ts +7 -0
  37. package/dist/common/splines-clip.d.ts.map +1 -1
  38. package/dist/common/splines-geom.d.ts +8 -0
  39. package/dist/common/splines-geom.d.ts.map +1 -1
  40. package/dist/common/splines-routespl.d.ts.map +1 -1
  41. package/dist/common/textmeasure-lookup.d.ts.map +1 -1
  42. package/dist/common/textmeasure.d.ts.map +1 -1
  43. package/dist/gvc/anchor.d.ts.map +1 -1
  44. package/dist/gvc/context.d.ts +6 -0
  45. package/dist/gvc/context.d.ts.map +1 -1
  46. package/dist/gvc/device-cluster.d.ts +15 -0
  47. package/dist/gvc/device-cluster.d.ts.map +1 -1
  48. package/dist/gvc/device-labels.d.ts +14 -0
  49. package/dist/gvc/device-labels.d.ts.map +1 -1
  50. package/dist/gvc/device.d.ts.map +1 -1
  51. package/dist/gvc/first-layer.d.ts +9 -0
  52. package/dist/gvc/first-layer.d.ts.map +1 -0
  53. package/dist/gvc/gvrender-set-style.d.ts +28 -0
  54. package/dist/gvc/gvrender-set-style.d.ts.map +1 -0
  55. package/dist/gvc/usershape.d.ts +18 -0
  56. package/dist/gvc/usershape.d.ts.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +3169 -1296
  59. package/dist/index.js.map +4 -4
  60. package/dist/layout/dot/cluster.d.ts.map +1 -1
  61. package/dist/layout/dot/compound.d.ts.map +1 -1
  62. package/dist/layout/dot/conc.d.ts.map +1 -1
  63. package/dist/layout/dot/decomp.d.ts.map +1 -1
  64. package/dist/layout/dot/edge-route-clip.d.ts +32 -14
  65. package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
  66. package/dist/layout/dot/edge-route-routing.d.ts.map +1 -1
  67. package/dist/layout/dot/mincross-build.d.ts.map +1 -1
  68. package/dist/layout/dot/mincross-cross.d.ts.map +1 -1
  69. package/dist/layout/dot/rank.d.ts.map +1 -1
  70. package/dist/layout/dot/splines.d.ts.map +1 -1
  71. package/dist/layout/fdp/index.d.ts.map +1 -1
  72. package/dist/layout/fdp/tlayout-parms.d.ts.map +1 -1
  73. package/dist/layout/fdp/xlayout.d.ts.map +1 -1
  74. package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts +11 -0
  75. package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts.map +1 -0
  76. package/dist/layout/neato/__fixtures__/hier-imds.d.ts +36 -0
  77. package/dist/layout/neato/__fixtures__/hier-imds.d.ts.map +1 -0
  78. package/dist/layout/neato/__fixtures__/hier-majorization.d.ts +32 -0
  79. package/dist/layout/neato/__fixtures__/hier-majorization.d.ts.map +1 -0
  80. package/dist/layout/neato/cdt-surface.d.ts.map +1 -1
  81. package/dist/layout/neato/compute-hierarchy.d.ts +24 -0
  82. package/dist/layout/neato/compute-hierarchy.d.ts.map +1 -0
  83. package/dist/layout/neato/conjgrad-mkernel.d.ts +20 -0
  84. package/dist/layout/neato/conjgrad-mkernel.d.ts.map +1 -0
  85. package/dist/layout/neato/conjgrad.d.ts +19 -0
  86. package/dist/layout/neato/conjgrad.d.ts.map +1 -1
  87. package/dist/layout/neato/constrained-majorization.d.ts +26 -0
  88. package/dist/layout/neato/constrained-majorization.d.ts.map +1 -0
  89. package/dist/layout/neato/dijkstra.d.ts +33 -16
  90. package/dist/layout/neato/dijkstra.d.ts.map +1 -1
  91. package/dist/layout/neato/edge-len.d.ts.map +1 -1
  92. package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
  93. package/dist/layout/neato/gts-triangle.d.ts +14 -0
  94. package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
  95. package/dist/layout/neato/init.d.ts +6 -22
  96. package/dist/layout/neato/init.d.ts.map +1 -1
  97. package/dist/layout/neato/kk.d.ts.map +1 -1
  98. package/dist/layout/neato/kkutils.d.ts +20 -0
  99. package/dist/layout/neato/kkutils.d.ts.map +1 -0
  100. package/dist/layout/neato/make-graph-data.d.ts +43 -0
  101. package/dist/layout/neato/make-graph-data.d.ts.map +1 -0
  102. package/dist/layout/neato/matrix-ops.d.ts +46 -0
  103. package/dist/layout/neato/matrix-ops.d.ts.map +1 -1
  104. package/dist/layout/neato/multispline-router.d.ts.map +1 -1
  105. package/dist/layout/neato/multispline.d.ts.map +1 -1
  106. package/dist/layout/neato/opt-arrangement.d.ts +20 -0
  107. package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
  108. package/dist/layout/neato/poly.d.ts.map +1 -1
  109. package/dist/layout/neato/quad-prog-solve.d.ts +44 -0
  110. package/dist/layout/neato/quad-prog-solve.d.ts.map +1 -0
  111. package/dist/layout/neato/sgd.d.ts.map +1 -1
  112. package/dist/layout/neato/smart-ini-x.d.ts +9 -0
  113. package/dist/layout/neato/smart-ini-x.d.ts.map +1 -0
  114. package/dist/layout/neato/splines.d.ts +1 -1
  115. package/dist/layout/neato/splines.d.ts.map +1 -1
  116. package/dist/layout/neato/start.d.ts +4 -1
  117. package/dist/layout/neato/start.d.ts.map +1 -1
  118. package/dist/layout/neato/stress.d.ts +8 -0
  119. package/dist/layout/neato/stress.d.ts.map +1 -1
  120. package/dist/model/edge.d.ts +1 -1
  121. package/dist/model/edge.d.ts.map +1 -1
  122. package/dist/model/lazy-attrs.d.ts +10 -0
  123. package/dist/model/lazy-attrs.d.ts.map +1 -0
  124. package/dist/model/node.d.ts +1 -1
  125. package/dist/model/node.d.ts.map +1 -1
  126. package/dist/ortho/index.d.ts.map +1 -1
  127. package/dist/parser/index.d.ts.map +1 -1
  128. package/dist/render/color-resolve.d.ts +49 -1
  129. package/dist/render/color-resolve.d.ts.map +1 -1
  130. package/dist/render/dot/edge-draw.d.ts +1 -1
  131. package/dist/render/dot/edge-draw.d.ts.map +1 -1
  132. package/dist/render/public.d.ts.map +1 -1
  133. package/dist/render/svg-edge-split.d.ts +2 -0
  134. package/dist/render/svg-edge-split.d.ts.map +1 -1
  135. package/dist/render/svg-multicolor.d.ts +2 -2
  136. package/dist/render/svg-multicolor.d.ts.map +1 -1
  137. package/dist/render/xdot-public.d.ts.map +1 -1
  138. package/dist/render.js +2799 -927
  139. package/dist/render.js.map +4 -4
  140. package/package.json +1 -1
  141. package/src/async/render-async.ts +6 -0
  142. package/src/common/agerr.ts +96 -0
  143. package/src/common/arrows-shapes-poly.ts +19 -5
  144. package/src/common/color.ts +38 -10
  145. package/src/common/crand.ts +8 -2
  146. package/src/common/emit-once.ts +27 -0
  147. package/src/common/graph-init.ts +2 -1
  148. package/src/common/htmltable-attr-warn.ts +3 -4
  149. package/src/common/htmltable-emit-fill.ts +104 -11
  150. package/src/common/htmltable-emit-rules.ts +2 -0
  151. package/src/common/htmltable-emit.ts +4 -3
  152. package/src/common/htmltable-lex-expat.ts +96 -23
  153. package/src/common/htmltable-pos-runs.ts +11 -6
  154. package/src/common/htmltable.ts +28 -117
  155. package/src/common/libm-trig.ts +43 -0
  156. package/src/common/multicolor.ts +88 -24
  157. package/src/common/poly-gencode.ts +30 -10
  158. package/src/common/poly-init.ts +20 -1
  159. package/src/common/postproc.ts +4 -1
  160. package/src/common/random.ts +11 -1
  161. package/src/common/render-state.ts +36 -0
  162. package/src/common/splines-clip.ts +15 -12
  163. package/src/common/splines-geom.ts +8 -4
  164. package/src/common/splines-routespl.ts +2 -1
  165. package/src/common/textmeasure-lookup.ts +9 -2
  166. package/src/common/textmeasure.ts +45 -2
  167. package/src/gvc/anchor.ts +1 -12
  168. package/src/gvc/context.ts +8 -0
  169. package/src/gvc/device-cluster.ts +93 -10
  170. package/src/gvc/device-labels.ts +27 -0
  171. package/src/gvc/device.ts +34 -8
  172. package/src/gvc/first-layer.ts +26 -0
  173. package/src/gvc/gvrender-set-style.ts +114 -0
  174. package/src/gvc/usershape.ts +26 -0
  175. package/src/index.ts +3 -0
  176. package/src/layout/dot/cluster.ts +2 -1
  177. package/src/layout/dot/compound.ts +3 -4
  178. package/src/layout/dot/conc.ts +6 -5
  179. package/src/layout/dot/decomp.ts +3 -0
  180. package/src/layout/dot/edge-route-clip.ts +46 -28
  181. package/src/layout/dot/edge-route-routing.ts +11 -3
  182. package/src/layout/dot/fastgr.ts +2 -2
  183. package/src/layout/dot/mincross-build.ts +2 -0
  184. package/src/layout/dot/mincross-cross.ts +2 -0
  185. package/src/layout/dot/rank.ts +2 -0
  186. package/src/layout/dot/splines.ts +2 -1
  187. package/src/layout/fdp/index.ts +2 -1
  188. package/src/layout/fdp/tlayout-parms.ts +9 -2
  189. package/src/layout/fdp/xlayout.ts +10 -2
  190. package/src/layout/neato/__fixtures__/dijkstra-f.ts +17 -0
  191. package/src/layout/neato/__fixtures__/hier/c1554.gv +33 -0
  192. package/src/layout/neato/__fixtures__/hier/c1554.plain +26 -0
  193. package/src/layout/neato/__fixtures__/hier/c1554.xdot +161 -0
  194. package/src/layout/neato/__fixtures__/hier/cases/cycle.gv +1 -0
  195. package/src/layout/neato/__fixtures__/hier/cases/cycle.plain +10 -0
  196. package/src/layout/neato/__fixtures__/hier/cases/cycle.stderr +0 -0
  197. package/src/layout/neato/__fixtures__/hier/cases/dirnone.gv +1 -0
  198. package/src/layout/neato/__fixtures__/hier/cases/dirnone.plain +11 -0
  199. package/src/layout/neato/__fixtures__/hier/cases/dirnone.stderr +0 -0
  200. package/src/layout/neato/__fixtures__/hier/cases/epsilon.gv +1 -0
  201. package/src/layout/neato/__fixtures__/hier/cases/epsilon.plain +13 -0
  202. package/src/layout/neato/__fixtures__/hier/cases/epsilon.stderr +0 -0
  203. package/src/layout/neato/__fixtures__/hier/cases/levelsgap.gv +1 -0
  204. package/src/layout/neato/__fixtures__/hier/cases/levelsgap.plain +8 -0
  205. package/src/layout/neato/__fixtures__/hier/cases/levelsgap.stderr +0 -0
  206. package/src/layout/neato/__fixtures__/hier/cases/mds.gv +1 -0
  207. package/src/layout/neato/__fixtures__/hier/cases/mds.plain +10 -0
  208. package/src/layout/neato/__fixtures__/hier/cases/mds.stderr +0 -0
  209. package/src/layout/neato/__fixtures__/hier/cases/multiedge.gv +1 -0
  210. package/src/layout/neato/__fixtures__/hier/cases/multiedge.plain +10 -0
  211. package/src/layout/neato/__fixtures__/hier/cases/multiedge.stderr +0 -0
  212. package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.gv +1 -0
  213. package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.plain +8 -0
  214. package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.stderr +0 -0
  215. package/src/layout/neato/__fixtures__/hier/cases/startrandom.gv +1 -0
  216. package/src/layout/neato/__fixtures__/hier/cases/startrandom.plain +10 -0
  217. package/src/layout/neato/__fixtures__/hier/cases/startrandom.stderr +0 -0
  218. package/src/layout/neato/__fixtures__/hier/cases/startregular.gv +1 -0
  219. package/src/layout/neato/__fixtures__/hier/cases/startregular.plain +10 -0
  220. package/src/layout/neato/__fixtures__/hier/cases/startregular.stderr +0 -0
  221. package/src/layout/neato/__fixtures__/hier/cases/tdist.gv +1 -0
  222. package/src/layout/neato/__fixtures__/hier/cases/tdist.plain +13 -0
  223. package/src/layout/neato/__fixtures__/hier/cases/tdist.stderr +0 -0
  224. package/src/layout/neato/__fixtures__/hier/cases/undirected.gv +1 -0
  225. package/src/layout/neato/__fixtures__/hier/cases/undirected.plain +10 -0
  226. package/src/layout/neato/__fixtures__/hier/cases/undirected.stderr +0 -0
  227. package/src/layout/neato/__fixtures__/hier/hier-probe.gv +1 -0
  228. package/src/layout/neato/__fixtures__/hier/hier-probe.plain +16 -0
  229. package/src/layout/neato/__fixtures__/hier/hier-probe.xdot +62 -0
  230. package/src/layout/neato/__fixtures__/hier-imds.ts +151 -0
  231. package/src/layout/neato/__fixtures__/hier-majorization.ts +145 -0
  232. package/src/layout/neato/cdt-surface.ts +12 -22
  233. package/src/layout/neato/compute-hierarchy.ts +90 -0
  234. package/src/layout/neato/conjgrad-mkernel.ts +141 -0
  235. package/src/layout/neato/conjgrad.ts +208 -0
  236. package/src/layout/neato/constrained-majorization.ts +479 -0
  237. package/src/layout/neato/dijkstra.ts +218 -92
  238. package/src/layout/neato/edge-len.ts +2 -1
  239. package/src/layout/neato/fdp-adjust.ts +3 -2
  240. package/src/layout/neato/gts-triangle.ts +42 -0
  241. package/src/layout/neato/init.ts +68 -68
  242. package/src/layout/neato/kk.ts +2 -1
  243. package/src/layout/neato/kkutils.ts +96 -0
  244. package/src/layout/neato/make-graph-data.ts +166 -0
  245. package/src/layout/neato/matrix-ops.ts +138 -0
  246. package/src/layout/neato/multispline-router.ts +3 -3
  247. package/src/layout/neato/multispline.ts +3 -2
  248. package/src/layout/neato/opt-arrangement.ts +86 -0
  249. package/src/layout/neato/poly.ts +3 -2
  250. package/src/layout/neato/quad-prog-solve.ts +489 -0
  251. package/src/layout/neato/sgd.ts +5 -2
  252. package/src/layout/neato/smart-ini-x.ts +503 -0
  253. package/src/layout/neato/splines.ts +4 -4
  254. package/src/layout/neato/start.ts +19 -14
  255. package/src/layout/neato/stress.ts +28 -0
  256. package/src/model/edge.ts +3 -2
  257. package/src/model/lazy-attrs.ts +36 -0
  258. package/src/model/node.ts +3 -2
  259. package/src/ortho/index.ts +2 -1
  260. package/src/parser/dot.js +4 -1
  261. package/src/parser/dot.pegjs +5 -1
  262. package/src/parser/index.ts +4 -3
  263. package/src/render/color-resolve.ts +193 -4
  264. package/src/render/dot/edge-draw.ts +6 -3
  265. package/src/render/dot.ts +1 -1
  266. package/src/render/public.ts +2 -0
  267. package/src/render/svg-edge-split.ts +6 -4
  268. package/src/render/svg-multicolor.ts +4 -3
  269. package/src/render/svg-parallel-edge.ts +2 -2
  270. 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
- const dist = Math.hypot(
91
- g.nodes[t]!.ctr.x - g.nodes[h]!.ctr.x,
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
- console.warn(
343
- `Warning: Could not create control points for multiple spline for edge (${e.tail.name},${e.head.name})`,
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
+ }
@@ -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
- console.error(`Error: makeAddPoly: unknown shape type ${shapeName(n)}\n`);
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
- console.error(`Error: makePoly: unknown shape type ${shapeName(n)}\n`);
300
+ agerrorf(`makePoly: unknown shape type ${shapeName(n)}\n`);
300
301
  return null;
301
302
  }
302
303
  }