@knowvah/dot-engine 1.1.0 → 1.2.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 (61) hide show
  1. package/README.md +48 -17
  2. package/dist/api.js +24 -0
  3. package/dist/api.js.map +2 -2
  4. package/dist/common/arrows.d.ts.map +1 -1
  5. package/dist/common/edge-label-init.d.ts.map +1 -1
  6. package/dist/common/htmltable-emit.d.ts +0 -8
  7. package/dist/common/htmltable-emit.d.ts.map +1 -1
  8. package/dist/common/ps-fontalias.d.ts +22 -9
  9. package/dist/common/ps-fontalias.d.ts.map +1 -1
  10. package/dist/common/splines-clip.d.ts.map +1 -1
  11. package/dist/gvc/device.d.ts.map +1 -1
  12. package/dist/gvc/job.d.ts +4 -0
  13. package/dist/gvc/job.d.ts.map +1 -1
  14. package/dist/index.js +1981 -1533
  15. package/dist/index.js.map +4 -4
  16. package/dist/layout/dot/index.d.ts +1 -1
  17. package/dist/layout/dot/index.d.ts.map +1 -1
  18. package/dist/layout/sfdp/init.d.ts.map +1 -1
  19. package/dist/model/graph.d.ts +24 -0
  20. package/dist/model/graph.d.ts.map +1 -1
  21. package/dist/parser/builder.d.ts +32 -0
  22. package/dist/parser/builder.d.ts.map +1 -1
  23. package/dist/render/dot/agwrite.d.ts +186 -0
  24. package/dist/render/dot/agwrite.d.ts.map +1 -0
  25. package/dist/render/dot/attrs.d.ts +220 -0
  26. package/dist/render/dot/attrs.d.ts.map +1 -0
  27. package/dist/render/dot/edge-draw.d.ts +60 -0
  28. package/dist/render/dot/edge-draw.d.ts.map +1 -0
  29. package/dist/render/dot/types.d.ts +28 -0
  30. package/dist/render/dot/types.d.ts.map +1 -0
  31. package/dist/render/dot/xdot-ops.d.ts +206 -0
  32. package/dist/render/dot/xdot-ops.d.ts.map +1 -0
  33. package/dist/render/dot.d.ts +58 -277
  34. package/dist/render/dot.d.ts.map +1 -1
  35. package/dist/render/map.d.ts +14 -1
  36. package/dist/render/map.d.ts.map +1 -1
  37. package/dist/render/svg-graph.d.ts.map +1 -1
  38. package/dist/render.js +1942 -1494
  39. package/dist/render.js.map +4 -4
  40. package/package.json +3 -1
  41. package/src/common/arrows.ts +17 -4
  42. package/src/common/edge-label-init.ts +9 -2
  43. package/src/common/htmltable-emit.ts +36 -8
  44. package/src/common/poly-gencode.ts +8 -8
  45. package/src/common/ps-fontalias.ts +80 -49
  46. package/src/common/splines-clip.ts +36 -10
  47. package/src/gvc/device.ts +18 -6
  48. package/src/gvc/job.ts +4 -0
  49. package/src/layout/dot/index.ts +37 -5
  50. package/src/layout/sfdp/init.ts +11 -0
  51. package/src/model/graph.ts +26 -0
  52. package/src/parser/builder.ts +90 -5
  53. package/src/render/dot/agwrite.ts +506 -0
  54. package/src/render/dot/attrs.ts +437 -0
  55. package/src/render/dot/edge-draw.ts +203 -0
  56. package/src/render/dot/types.ts +32 -0
  57. package/src/render/dot/xdot-ops.ts +432 -0
  58. package/src/render/dot.ts +114 -1131
  59. package/src/render/map.ts +18 -1
  60. package/src/render/svg-graph.ts +4 -0
  61. package/src/render/svg-helpers.ts +1 -1
@@ -0,0 +1,28 @@
1
+ /** Shared shapes for the dot/xdot renderer and its agwrite serializer. */
2
+ import type { Graph } from '../../model/graph.js';
3
+ import type { Node } from '../../model/node.js';
4
+ import type { Edge } from '../../model/edge.js';
5
+ /** Accumulated xdot draw strings for one model object (agset side-table). */
6
+ export interface XdotDraws {
7
+ draw?: string;
8
+ ldraw?: string;
9
+ hdraw?: string;
10
+ tdraw?: string;
11
+ hldraw?: string;
12
+ tldraw?: string;
13
+ }
14
+ /** Mutable state threaded through the recursive agwrite serializer. */
15
+ export interface SerCtx {
16
+ out: string[];
17
+ /** subgraph → preorder number (write.c:subgdfs). */
18
+ preorder: Map<Graph, number>;
19
+ /** node → preorder of the subgraph it was last written in (node_last_written). */
20
+ nodeLW: Map<Node, number>;
21
+ /** edge → preorder of the subgraph it was last written in (edge_last_written). */
22
+ edgeLW: Map<Edge, number>;
23
+ /** objects whose attributes have already been emitted (AGATTRWF/attrs_written). */
24
+ attrsWritten: Set<Node | Edge>;
25
+ /** current indentation depth. */
26
+ level: number;
27
+ }
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/render/dot/types.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAE1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,oDAAoD;IACpD,QAAQ,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7B,kFAAkF;IAClF,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,kFAAkF;IAClF,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,mFAAmF;IACnF,YAAY,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC/B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * xdot output primitives — number, string, colour and geometry formatting for
3
+ * the xdot draw-string language, plus the shared `%.5g`/`%.2f` helpers.
4
+ *
5
+ * @see plugin/core/gvrender_core_dot.c
6
+ */
7
+ import type { Point } from '../../model/geom.js';
8
+ import type { GVColor } from '../../common/color.js';
9
+ export declare const XDOT_VERSION = "1.7";
10
+ /** Max magnitude cap — matches maxnegnum in C source. */
11
+ export declare const MAX_NEGNUM = 1000000000000000;
12
+ /** Near-zero suppression threshold (same as gvprintdouble). */
13
+ export declare const NEAR_ZERO = 0.005;
14
+ /** Indices 8/9 alias 1; 10/11 alias 5. NUM_XBUFS covers 0–7. */
15
+ export declare const NUM_XBUFS = 8;
16
+ /**
17
+ * Style tokens that never become an xdot `S` op: `filled`/`bold`/`setlinewidth`
18
+ * are filtered by xdot_style itself; the polygon/fill styles are consumed by the
19
+ * shape/fill code before gvrender_set_style, so they never reach the render
20
+ * style. Only line styles (solid/dashed/dotted + unknown) emit an `S` op.
21
+ * @see plugin/core/gvrender_core_dot.c:184 xdot_style · lib/common/shapes.c
22
+ */
23
+ export declare const NON_LINE_STYLES: ReadonlySet<string>;
24
+ /**
25
+ * Strip trailing zeros (and the decimal point) from a toFixed(3) string,
26
+ * then collapse a leading "0." or "-0." prefix.
27
+ */
28
+ export declare function trimAndStrip(s: string): string;
29
+ /**
30
+ * Convert a double to compact string form for DOT/XDOT output.
31
+ *
32
+ * Rules (porting gvprintnum from lib/gvc/gvdevice.c):
33
+ * - |n| > MAX_NEGNUM → clamped to ±MAX_NEGNUM
34
+ * - |n| < NEAR_ZERO → "0" (suppresses -0)
35
+ * - 3 decimal places, trailing zeros and point stripped
36
+ * - Leading "0." collapsed to "." (e.g. 0.5 → ".5")
37
+ *
38
+ * Exported for json.ts (T30) and map.ts (T31).
39
+ *
40
+ * @see lib/gvc/gvdevice.c:gvprintnum
41
+ */
42
+ export declare function printNum(n: number): string;
43
+ /**
44
+ * Create a 12-slot xbufs array with the canonical aliasing.
45
+ *
46
+ * Indices 8–9 (NDraw, EDraw) alias index 1 (CDraw).
47
+ * Indices 10–11 (NLabel, ELabel) alias index 5 (CLabel).
48
+ *
49
+ * @see plugin/core/gvrender_core_dot.c:xbufs
50
+ */
51
+ export declare function makeXbufs(): string[][];
52
+ /**
53
+ * Format one xdot draw-op number: 2 decimals, trailing zeros and point trimmed
54
+ * — mirroring xdot_fmt_num ("%.02f" + agxbuf_trim_zeros). Distinct from
55
+ * `printNum` (used for the DOT `pos`/`bb`/`width`/`height` attributes), which
56
+ * keeps more precision; xdot's DRAW ops are emitted at 2 dp by the C engine.
57
+ * @see plugin/core/gvrender_core_dot.c:126 xdot_fmt_num
58
+ */
59
+ export declare function xdotNum(v: number): string;
60
+ /**
61
+ * Format a single xdot point "x y ". xdot is y-up: `Y_invert` defaults false, so
62
+ * `yDir(y, yOff)` returns `y` unchanged for xdot (only `-Ty` plain/dot invert).
63
+ * The layout coordinate passes through with NO inversion — unlike the SVG path.
64
+ * @see lib/common/output.c:36 yDir · plugin/core/gvrender_core_dot.c:132 xdot_point
65
+ */
66
+ export declare function xdotPoint(p: Point): string;
67
+ /** Format N points preceded by opcode and count: "<c> <n> x0 y0 x1 y1 …". */
68
+ export declare function xdotPoints(c: string, pts: Point[]): string;
69
+ /**
70
+ * UTF-8 byte length of a string — the value C's `xdot_str` writes as the length
71
+ * prefix (`strlen(s)` over the UTF-8 bytes), NOT the JS UTF-16 code-unit count.
72
+ * A label like `ÿ` (U+00FF) is 2 UTF-8 bytes, so its `T`/`F` op prefix is 2.
73
+ * @see plugin/core/gvrender_core_dot.c:83 xdot_str_xbuf (`%zu`, strlen)
74
+ */
75
+ export declare function utf8Len(s: string): number;
76
+ /** Clamp a normalized [0,1] float channel to a 0-255 byte (round-to-nearest). */
77
+ export declare function chanByte(v: number): number;
78
+ /**
79
+ * Resolve a GVColor to RGBA bytes — the value the C xdot callbacks read from
80
+ * `job->obj->pencolor.u.rgba` (already resolved by gvrender_set_pencolor). A
81
+ * plain `rgba` passes through; named/hex/HSV specs run through colorxlate; a
82
+ * `none` color is fully transparent black (callers gate on PEN_NONE).
83
+ */
84
+ export declare function gvColorRgba(c: GVColor): [number, number, number, number];
85
+ /**
86
+ * Bare xdot color body (no `c `/`C ` prefix): the CONSTANT length prefix
87
+ * (7 for `#rrggbb`, 9 for `#rrggbbaa`) plus `-#hex`, the alpha byte present only
88
+ * when not fully opaque. Used both by the color ops and by gradient color stops.
89
+ * @see plugin/core/gvrender_core_dot.c:99 xdot_str_color_xbuf
90
+ */
91
+ export declare function xdotColorBody(rgba: [number, number, number, number]): string;
92
+ /**
93
+ * Format an xdot color op ("c "/"C ") from RGBA bytes.
94
+ * @see plugin/core/gvrender_core_dot.c:99 xdot_str_color_xbuf
95
+ */
96
+ export declare function xdotColorOp(prefix: 'c ' | 'C ', rgba: [number, number, number, number]): string;
97
+ /**
98
+ * Build the linear-gradient `C len -[x0 y0 x1 y1 2 <stops>]` fill op. Endpoints
99
+ * come from getGradientPoints (the same geometry the SVG gradient uses); stops
100
+ * are (frac,fill)/(frac,stop) when frac>0, else (0,fill)/(1,stop). Shared by
101
+ * node/cluster gradient fills and the graph-background gradient.
102
+ * @see plugin/core/gvrender_core_dot.c:544-598 xdot_gradient_fillcolor
103
+ */
104
+ export declare function linearGradientOp(pts: Point[], fillColor: GVColor, stopColor: GVColor, frac: number, angleDeg: number): string;
105
+ /** The `<frac> <colorbody>` stop pairs for a gradient (frac>0 vs the 0/1 form). */
106
+ export declare function gradientStops(fillColor: GVColor, stopColor: GVColor, frac: number): string;
107
+ /**
108
+ * Build the radial-gradient `C len -(c1x c1y r1 c2x c2y r2 2 <stops>)` fill op.
109
+ * Reuses getGradientPoints (radial) for the center/radii, un-negating its SVG
110
+ * y. r1 = outerR/4, r2 = outerR; c2 is the center, c1 the center offset by r1
111
+ * along the gradient angle (== center when angle 0).
112
+ * @see plugin/core/gvrender_core_dot.c:562-585 xdot_gradient_fillcolor (radial)
113
+ */
114
+ export declare function radialGradientOp(pts: Point[], fillColor: GVColor, stopColor: GVColor, frac: number, angleDeg: number): string;
115
+ /** Pen ("c ") color op from a resolved GVColor. */
116
+ export declare function xdotPenColor(c: GVColor): string;
117
+ /** Fill ("C ") color op from a resolved GVColor. */
118
+ export declare function xdotFillColor(c: GVColor): string;
119
+ /**
120
+ * Build the xdot "F size len -name " font op. Mirrors xdot_textspan's `F` +
121
+ * `xdot_str(job, "", font->name)` — the length prefix is the byte length of the
122
+ * face name. @see plugin/core/gvrender_core_dot.c:498 xdot_textspan
123
+ */
124
+ export declare function xdotFont(size: number, name: string): string;
125
+ /**
126
+ * Build an xdot length-prefixed string op ("S "/"" prefix): "<pfx><len> -<s> ".
127
+ * @see plugin/core/gvrender_core_dot.c:83 xdot_str_xbuf
128
+ */
129
+ export declare function xdotStrOp(prefix: string, s: string): string;
130
+ /**
131
+ * Quote a DOT identifier unless it is a bare id or numeral, mirroring agwrite's
132
+ * agcanonStr so the serialized graph reparses (the comparator reparses both
133
+ * sides). Only `"` is escaped (→ `\"`); a `\` is left as-is — it is already the
134
+ * start of a stored escape like `\n`/`\l` that agcanonStr keeps verbatim, so
135
+ * doubling it (`\\n`) would change the name (`a\n(b\n"c")` must stay `a\n…`, not
136
+ * `a\\n…`). Over-quoting a value native leaves bare is harmless: both parse to
137
+ * the same name. @see lib/cgraph/write.c:_agstrcanon (escapes '"', keeps '\')
138
+ *
139
+ * ONE DELIBERATE DIVERGENCE from _agstrcanon: an ODD trailing backslash run is
140
+ * padded to even. C copies it verbatim and appends the closing quote, so a name
141
+ * ending in a single `\` serializes as `"a\"` — the backslash escapes the quote
142
+ * and the output does not reparse. The DOT lexer cannot produce such a name
143
+ * (source `"a\"` never terminates), so this is unreachable from any file and no
144
+ * corpus id changes; it is reachable only through the programmatic API, where a
145
+ * caller supplies the string directly. Padding costs a name that round-trips to
146
+ * `a\\` instead of `a\`, which beats emitting a document that cannot be parsed.
147
+ * @see CodeQL "Incomplete string escaping or encoding" — the alert's own remedy
148
+ * (escape every `\`) is wrong here and breaks `\n`/`\l` parity.
149
+ */
150
+ export declare function xdotId(s: string): string;
151
+ /**
152
+ * Format a number as C's `%.5g` — 5 significant figures, trailing zeros and
153
+ * point trimmed, switching to `e±NN` (min 2 exponent digits) when the exponent
154
+ * is < -4 or ≥ 5. Native writes the DOT `pos`/`bb`/`width`/`height` attributes
155
+ * with `%.5g` (output.c:71/294/302), so large coordinates round to 5 sig figs
156
+ * (`2219962` → `2.2201e+06`); `printNum`'s fixed 3 dp keeps too much precision.
157
+ *
158
+ * Uses {@link printfSig} rather than `Number.prototype.toPrecision(5)` so
159
+ * exact-halfway values (e.g. 1399.25) round half-to-even like C's snprintf,
160
+ * not half-away-from-zero like `toPrecision`. @see docs proven case
161
+ * graphs-b786 (circo): pos coordinate 1399.25 → native "1399.2", `toPrecision`
162
+ * gave "1399.3".
163
+ * @see lib/common/output.c:71 (agxbprint "%.5g")
164
+ */
165
+ export declare function gfmt5(v: number): string;
166
+ /**
167
+ * Format a number as C's `%.2f` — fixed 2 decimals, half-to-even on exact ties.
168
+ * `attach_attrs` writes the graph-label size attributes `lwidth`/`lheight` in
169
+ * INCHES with `%.2f`, unlike every other computed attribute (which uses `%.5g`).
170
+ * @see lib/common/output.c:244-247 (agxbprint "%.2f", PS2INCH)
171
+ */
172
+ export declare function gfmt2(v: number): string;
173
+ /**
174
+ * Format a label position as the `x,y` pair every computed `*_lp` attribute
175
+ * uses: both coordinates at `%.5g`. C passes y through `yDir()`, which is the
176
+ * identity unless `Y_invert` — and `Y_invert` is only set by `-Ty` (plain/dot),
177
+ * never for xdot — so no inversion is applied here, exactly as the existing
178
+ * `pos`/`bb` emission does.
179
+ * @see lib/common/output.c:35 yDir · lib/common/output.c:241 (agxbprint "%.5g,%.5g")
180
+ */
181
+ export declare function lpStr(p: Point): string;
182
+ /**
183
+ * Escape backslashes in a LABEL draw string — C's put_escaping_backslashes,
184
+ * applied to the `_ldraw_`/`_hldraw_`/`_tldraw_` buffers (not the shape draws)
185
+ * before agset. A literal `\` in a label's text (e.g. `WXYZ\nabc`) becomes `\\`
186
+ * so it survives the DOT string round-trip; the T-op byte-length prefix stays on
187
+ * the UNescaped text. @see plugin/core/gvrender_core_dot.c:218 put_escaping_backslashes
188
+ */
189
+ export declare function escBackslash(s: string): string;
190
+ /** True if s[i..] starts an escape sequence agcanonStr keeps verbatim: a `\`
191
+ * followed by one of E G H L N T l n r \ ". @see lib/cgraph/write.c:is_escape */
192
+ export declare function isEscapeSeq(s: string, i: number): boolean;
193
+ /**
194
+ * Escape a value for a DOT attribute exactly as agwrite's agcanonStr does: a `"`
195
+ * becomes `\"` ONLY when it is not already part of an escape sequence, so an
196
+ * existing `\"` or `\\` in the value is passed through verbatim rather than
197
+ * double-escaped. For an already-backslash-doubled value (node/edge/graph labels,
198
+ * post put_escaping_backslashes) this is identical to a naive `"`→`\"` — every
199
+ * `"` follows a doubled `\\`, so none are ever part_of_escape. For raw cluster
200
+ * labels (agset, no put_escaping) it preserves the source `\"`/`\\` unchanged.
201
+ * @see lib/cgraph/write.c:_agstrcanon (135-167)
202
+ */
203
+ export declare function agcanonEscape(s: string): string;
204
+ /** Trim a "%.3f" fixed string like C's agxbuf_trim_zeros (trailing 0s + dot). */
205
+ export declare function trimFixed3(v: number): string;
206
+ //# sourceMappingURL=xdot-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xdot-ops.d.ts","sourceRoot":"","sources":["../../../src/render/dot/xdot-ops.ts"],"names":[],"mappings":"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"}
@@ -12,171 +12,14 @@ import type { Node } from '../model/node.js';
12
12
  import type { Edge } from '../model/edge.js';
13
13
  import type { Point } from '../model/geom.js';
14
14
  import type { TextSpan } from '../common/emit-types.js';
15
- import type { GVColor } from '../common/color.js';
16
15
  import type { RendererPlugin } from '../gvc/context.js';
16
+ import { EdgeDrawBase } from './dot/edge-draw.js';
17
+ export * from './dot/types.js';
18
+ export type { XdotDraws, SerCtx } from './dot/types.js';
19
+ import type { XdotDraws } from './dot/types.js';
17
20
  import type { RenderJob } from '../gvc/job.js';
18
- /**
19
- * Convert a double to compact string form for DOT/XDOT output.
20
- *
21
- * Rules (porting gvprintnum from lib/gvc/gvdevice.c):
22
- * - |n| > MAX_NEGNUM → clamped to ±MAX_NEGNUM
23
- * - |n| < NEAR_ZERO → "0" (suppresses -0)
24
- * - 3 decimal places, trailing zeros and point stripped
25
- * - Leading "0." collapsed to "." (e.g. 0.5 → ".5")
26
- *
27
- * Exported for json.ts (T30) and map.ts (T31).
28
- *
29
- * @see lib/gvc/gvdevice.c:gvprintnum
30
- */
31
- export declare function printNum(n: number): string;
32
- /**
33
- * Create a 12-slot xbufs array with the canonical aliasing.
34
- *
35
- * Indices 8–9 (NDraw, EDraw) alias index 1 (CDraw).
36
- * Indices 10–11 (NLabel, ELabel) alias index 5 (CLabel).
37
- *
38
- * @see plugin/core/gvrender_core_dot.c:xbufs
39
- */
40
- export declare function makeXbufs(): string[][];
41
- /**
42
- * Format one xdot draw-op number: 2 decimals, trailing zeros and point trimmed
43
- * — mirroring xdot_fmt_num ("%.02f" + agxbuf_trim_zeros). Distinct from
44
- * `printNum` (used for the DOT `pos`/`bb`/`width`/`height` attributes), which
45
- * keeps more precision; xdot's DRAW ops are emitted at 2 dp by the C engine.
46
- * @see plugin/core/gvrender_core_dot.c:126 xdot_fmt_num
47
- */
48
- export declare function xdotNum(v: number): string;
49
- /**
50
- * Format a single xdot point "x y ". xdot is y-up: `Y_invert` defaults false, so
51
- * `yDir(y, yOff)` returns `y` unchanged for xdot (only `-Ty` plain/dot invert).
52
- * The layout coordinate passes through with NO inversion — unlike the SVG path.
53
- * @see lib/common/output.c:36 yDir · plugin/core/gvrender_core_dot.c:132 xdot_point
54
- */
55
- export declare function xdotPoint(p: Point): string;
56
- /** Format N points preceded by opcode and count: "<c> <n> x0 y0 x1 y1 …". */
57
- export declare function xdotPoints(c: string, pts: Point[]): string;
58
- /**
59
- * UTF-8 byte length of a string — the value C's `xdot_str` writes as the length
60
- * prefix (`strlen(s)` over the UTF-8 bytes), NOT the JS UTF-16 code-unit count.
61
- * A label like `ÿ` (U+00FF) is 2 UTF-8 bytes, so its `T`/`F` op prefix is 2.
62
- * @see plugin/core/gvrender_core_dot.c:83 xdot_str_xbuf (`%zu`, strlen)
63
- */
64
- export declare function utf8Len(s: string): number;
65
- /**
66
- * Resolve a GVColor to RGBA bytes — the value the C xdot callbacks read from
67
- * `job->obj->pencolor.u.rgba` (already resolved by gvrender_set_pencolor). A
68
- * plain `rgba` passes through; named/hex/HSV specs run through colorxlate; a
69
- * `none` color is fully transparent black (callers gate on PEN_NONE).
70
- */
71
- export declare function gvColorRgba(c: GVColor): [number, number, number, number];
72
- /**
73
- * Bare xdot color body (no `c `/`C ` prefix): the CONSTANT length prefix
74
- * (7 for `#rrggbb`, 9 for `#rrggbbaa`) plus `-#hex`, the alpha byte present only
75
- * when not fully opaque. Used both by the color ops and by gradient color stops.
76
- * @see plugin/core/gvrender_core_dot.c:99 xdot_str_color_xbuf
77
- */
78
- export declare function xdotColorBody(rgba: [number, number, number, number]): string;
79
- /**
80
- * Format an xdot color op ("c "/"C ") from RGBA bytes.
81
- * @see plugin/core/gvrender_core_dot.c:99 xdot_str_color_xbuf
82
- */
83
- export declare function xdotColorOp(prefix: 'c ' | 'C ', rgba: [number, number, number, number]): string;
84
- /**
85
- * Build the linear-gradient `C len -[x0 y0 x1 y1 2 <stops>]` fill op. Endpoints
86
- * come from getGradientPoints (the same geometry the SVG gradient uses); stops
87
- * are (frac,fill)/(frac,stop) when frac>0, else (0,fill)/(1,stop). Shared by
88
- * node/cluster gradient fills and the graph-background gradient.
89
- * @see plugin/core/gvrender_core_dot.c:544-598 xdot_gradient_fillcolor
90
- */
91
- export declare function linearGradientOp(pts: Point[], fillColor: GVColor, stopColor: GVColor, frac: number, angleDeg: number): string;
92
- /**
93
- * Build the radial-gradient `C len -(c1x c1y r1 c2x c2y r2 2 <stops>)` fill op.
94
- * Reuses getGradientPoints (radial) for the center/radii, un-negating its SVG
95
- * y. r1 = outerR/4, r2 = outerR; c2 is the center, c1 the center offset by r1
96
- * along the gradient angle (== center when angle 0).
97
- * @see plugin/core/gvrender_core_dot.c:562-585 xdot_gradient_fillcolor (radial)
98
- */
99
- export declare function radialGradientOp(pts: Point[], fillColor: GVColor, stopColor: GVColor, frac: number, angleDeg: number): string;
100
- /** Pen ("c ") color op from a resolved GVColor. */
101
- export declare function xdotPenColor(c: GVColor): string;
102
- /** Fill ("C ") color op from a resolved GVColor. */
103
- export declare function xdotFillColor(c: GVColor): string;
104
- /**
105
- * Build the xdot "F size len -name " font op. Mirrors xdot_textspan's `F` +
106
- * `xdot_str(job, "", font->name)` — the length prefix is the byte length of the
107
- * face name. @see plugin/core/gvrender_core_dot.c:498 xdot_textspan
108
- */
109
- export declare function xdotFont(size: number, name: string): string;
110
- /**
111
- * Quote a DOT identifier unless it is a bare id or numeral, mirroring agwrite's
112
- * agcanonStr so the serialized graph reparses (the comparator reparses both
113
- * sides). Only `"` is escaped (→ `\"`); a `\` is left as-is — it is already the
114
- * start of a stored escape like `\n`/`\l` that agcanonStr keeps verbatim, so
115
- * doubling it (`\\n`) would change the name (`a\n(b\n"c")` must stay `a\n…`, not
116
- * `a\\n…`). Over-quoting a value native leaves bare is harmless: both parse to
117
- * the same name. @see lib/cgraph/write.c:_agstrcanon (escapes '"', keeps '\')
118
- */
119
- export declare function xdotId(s: string): string;
120
- /**
121
- * Format a number as C's `%.5g` — 5 significant figures, trailing zeros and
122
- * point trimmed, switching to `e±NN` (min 2 exponent digits) when the exponent
123
- * is < -4 or ≥ 5. Native writes the DOT `pos`/`bb`/`width`/`height` attributes
124
- * with `%.5g` (output.c:71/294/302), so large coordinates round to 5 sig figs
125
- * (`2219962` → `2.2201e+06`); `printNum`'s fixed 3 dp keeps too much precision.
126
- *
127
- * Uses {@link printfSig} rather than `Number.prototype.toPrecision(5)` so
128
- * exact-halfway values (e.g. 1399.25) round half-to-even like C's snprintf,
129
- * not half-away-from-zero like `toPrecision`. @see docs proven case
130
- * graphs-b786 (circo): pos coordinate 1399.25 → native "1399.2", `toPrecision`
131
- * gave "1399.3".
132
- * @see lib/common/output.c:71 (agxbprint "%.5g")
133
- */
134
- export declare function gfmt5(v: number): string;
135
- /** Format node attributes for DOT output. */
136
- export declare function formatNodeAttrs(n: Node): string;
137
- /**
138
- * Format edge spline points for the DOT `pos` attribute. Per bezier: the start
139
- * endpoint `s,sp` when `sflag` set, then the end endpoint `e,ep` when `eflag`
140
- * set, then `bez.size` control points — all at `%.5g`, exactly as native's
141
- * spline serialization. @see lib/common/output.c:357-372
142
- */
143
- export declare function formatEdgePos(e: Edge): string;
144
- /** Return the edge connector token. */
145
- export declare function edgeConnector(directed: boolean): string;
146
- /** Return true if the graph is directed or strict-directed. */
147
- export declare function isDirected(g: Graph): boolean;
148
- /** Emit the opening digraph/graph line and bb attribute. */
149
- export declare function dotBeginGraphHeader(g: Graph, job: RenderJob): void;
150
- /**
151
- * DOT format renderer — writes graphviz DOT language with layout attributes.
152
- *
153
- * @see plugin/core/gvrender_core_dot.c:dot_engine
154
- */
155
- export declare class DotRenderer implements RendererPlugin {
156
- readonly type = "dot";
157
- /** @see plugin/core/gvplugin_core.c registration table */
158
- readonly quality = 0;
159
- beginGraph(g: Graph, job: RenderJob): void;
160
- endGraph(_g: Graph, job: RenderJob): void;
161
- beginNode(_n: Node, _job: RenderJob): void;
162
- endNode(n: Node, job: RenderJob): void;
163
- beginEdge(_e: Edge, _job: RenderJob): void;
164
- endEdge(e: Edge, job: RenderJob): void;
165
- textspan(_pos: Point, _span: TextSpan, _job: RenderJob): void;
166
- ellipse(_c: Point, _rx: number, _ry: number, _f: boolean, _j: RenderJob): void;
167
- polygon(_pts: Point[], _filled: boolean, _job: RenderJob): void;
168
- bezier(_pts: Point[], _filled: boolean, _job: RenderJob): void;
169
- polyline(_pts: Point[], _job: RenderJob): void;
170
- }
171
- /** Accumulated xdot draw strings for one model object (agset side-table). */
172
- export interface XdotDraws {
173
- draw?: string;
174
- ldraw?: string;
175
- hdraw?: string;
176
- tdraw?: string;
177
- hldraw?: string;
178
- tldraw?: string;
179
- }
21
+ export * from './dot/xdot-ops.js';
22
+ export * from './dot/attrs.js';
180
23
  /**
181
24
  * XDOT format renderer — mirrors plugin/core/gvrender_core_dot.c's xdot engine.
182
25
  *
@@ -192,27 +35,33 @@ export interface XdotDraws {
192
35
  *
193
36
  * @see plugin/core/gvrender_core_dot.c xdot_begin_graph / xdot_end_graph
194
37
  */
195
- export declare class XdotRenderer implements RendererPlugin {
196
- readonly type = "xdot";
38
+ export declare class XdotRenderer extends EdgeDrawBase implements RendererPlugin {
39
+ readonly type: string;
197
40
  /** @see plugin/core/gvplugin_core.c registration table */
198
41
  readonly quality = 0;
42
+ /**
43
+ * Whether this format attaches the xdot draw attributes before agwrite.
44
+ * Mirrors C's `job->render.id` switch: FORMAT_DOT runs `attach_attrs(g)` then
45
+ * `agwrite`, while FORMAT_XDOT runs `attach_attrs_and_arrows` +
46
+ * `xdot_begin_graph`/`xdot_end_graph` and *then* the same `agwrite`. The
47
+ * serializer below is that shared `agwrite`; this flag is the only difference.
48
+ * @see plugin/core/gvrender_core_dot.c:404,418,475,482
49
+ */
50
+ protected readonly emitDraws: boolean;
51
+ /** Draw strings for `obj`, or undefined in a format that emits none (`-Tdot`).
52
+ * Single gate so the xdot path cannot drift from the dot path. */
53
+ protected drawsOf(obj: Node | Edge | Graph): XdotDraws | undefined;
199
54
  /** Per-render draw buffers. Indices 8/9 alias 1; 10/11 alias 5. */
200
55
  private bufs;
201
- /** setlinewidth state per emit_state. @see gvrender_core_dot.c penwidth[] */
202
- private penwidth;
203
56
  /** text-flag state per emit_state. @see gvrender_core_dot.c textflags[] */
204
57
  private textflags;
205
58
  /** Accumulated draw strings keyed by model object (C: agset on the object). */
206
59
  private draws;
207
- /** Clusters in render order — the graphs `endCluster` was actually called for
208
- * (from `renderClusters`/`GD_clust`), which is NOT always reachable via
209
- * `g.subgraphs`, so it is tracked here rather than re-walked. */
210
- private clusters;
211
60
  /** edge_in_box gate for the current edge: emit_edge draws nothing (spline OR
212
61
  * label) for an edge whose content is outside job->clip. Mirrors svg.ts's
213
62
  * edgeGroupOpen. @see lib/common/emit.c:emit_edge (3039) */
214
63
  private edgeDrawable;
215
- beginGraph(g: Graph, _job: RenderJob): void;
64
+ beginGraph(_g: Graph, _job: RenderJob): void;
216
65
  /** The per-object draw strings accumulated during the last render, keyed by
217
66
  * the ORIGINAL model object. These are the pre-serialization values C's
218
67
  * `-Tjson` reads directly (via agxget) — feeding them to parseXDot avoids the
@@ -223,6 +72,10 @@ export declare class XdotRenderer implements RendererPlugin {
223
72
  /** Emit the canvas background polygon into the GDRAW buffer.
224
73
  * @see lib/common/emit.c:1476 emit_background */
225
74
  pageBackground(g: Graph, job: RenderJob): void;
75
+ /** The canvas fill op: a two-colour `bgcolor` is a gradient (emit_background →
76
+ * findStopColor), linear or radial per style; anything else is a flat fill.
77
+ * @see lib/common/emit.c:1476 emit_background */
78
+ private backgroundFillOp;
226
79
  beginNode(_n: Node, _job: RenderJob): void;
227
80
  endNode(n: Node, _job: RenderJob): void;
228
81
  /**
@@ -240,27 +93,14 @@ export declare class XdotRenderer implements RendererPlugin {
240
93
  * -tapered` (the style), a transparent pen, the edge-color fill, then the
241
94
  * polygon vertices from `taper()` (y-up). @see lib/common/emit.c:2422
242
95
  */
243
- private emitTaperedSpline;
244
- /**
245
- * Emit a split-along-length `;` multicolor edge spline: split each routed
246
- * bezier along its arc length into one sub-curve per color segment, drawn
247
- * under that segment's pen. Reuses the same split geometry
248
- * (splitSplineByColor) as the SVG path. @see lib/common/emit.c:1975 multicolor
249
- */
250
- private emitSplitSpline;
251
- /**
252
- * Emit the parallel-multicolor edge spline: one offset Bézier per color,
253
- * offset SEP=2.0 perpendicular per pass — reusing the same offset geometry
254
- * (buildOffsetLists/advanceTmpList) as the SVG parallel-edge path.
255
- * @see lib/common/emit.c:2443 (parallel multicolor) / svg-parallel-edge.ts
256
- */
257
- private emitParallelSpline;
258
- /** Emit one arrow's primitive ops into `buf` (pen/fill from the edge color).
259
- * arrow_gen sets the default line style ("solid") and the edge penwidth
260
- * before each primitive, so a non-default penwidth emits `S setlinewidth(N)`
261
- * once (tracked per HDRAW/TDRAW state). @see arrows.c:arrow_gen */
262
- private emitArrows;
263
96
  endEdge(e: Edge, job: RenderJob): void;
97
+ /** Flush the six edge buffers and attach the non-empty ones. Label buffers get
98
+ * put_escaping_backslashes; shape buffers do not.
99
+ * @see plugin/core/gvrender_core_dot.c:218 */
100
+ private attachEdgeDraws;
101
+ /** Per-edge emit-state reset, run on both the drawable and the early-return
102
+ * path — identical in C, so it is one helper here rather than two copies. */
103
+ private resetEdgeState;
264
104
  beginCluster(_sg: Graph, _job: RenderJob): void;
265
105
  endCluster(sg: Graph, _job: RenderJob): void;
266
106
  textspan(pos: Point, span: TextSpan, job: RenderJob): void;
@@ -269,7 +109,7 @@ export declare class XdotRenderer implements RendererPlugin {
269
109
  bezier(pts: Point[], filled: boolean, job: RenderJob): void;
270
110
  polyline(pts: Point[], job: RenderJob): void;
271
111
  /** Pen color op from the resolved graphics state (default black). */
272
- private penOp;
112
+ protected penOp(job: RenderJob): string;
273
113
  /**
274
114
  * Fill op from the resolved graphics state. A linear gradient emits the
275
115
  * bracketed `C len -[G0 G1 2 <stops>]` form (xdot_gradient_fillcolor); a plain
@@ -280,7 +120,7 @@ export declare class XdotRenderer implements RendererPlugin {
280
120
  private fillOp;
281
121
  /** Style ops (`S`): setlinewidth on a penwidth change, plus dash/dot pen.
282
122
  * @see gvrender_core_dot.c:161 xdot_style */
283
- private styleOp;
123
+ protected styleOp(job: RenderJob): string;
284
124
  /** Active xdot buffer for the current emit state. */
285
125
  private getBuf;
286
126
  /** Join and clear the buffer for `state`; returns '' when empty. */
@@ -288,89 +128,30 @@ export declare class XdotRenderer implements RendererPlugin {
288
128
  /** Reset per-emit-state penwidth/textflags after an object (C's reset). */
289
129
  private resetState;
290
130
  private drawsFor;
291
- /**
292
- * Emit `key="value"`, escaping the value the way agwrite's agcanonStr does: a
293
- * `"` becomes `\"` unless it is already part of an escape sequence (see
294
- * agcanonEscape). A draw string carries label text that may contain a bare `"`
295
- * (would close the attribute early) or a source `\"`/`\\` (must not be
296
- * double-escaped). The byte-length prefix stays on the UNescaped text (the
297
- * parser un-escapes before parseXDot re-reads it), matching native exactly.
298
- * @see lib/cgraph/write.c:_agstrcanon (135-167)
299
- */
300
- private drawAttr;
301
- /** `llx,lly,urx,ury` from a graph's layout bb. */
302
- private bbStr;
303
- /**
304
- * Serialize the whole laid-out graph to xdot DOT text — a faithful port of
305
- * cgraph's agwrite (lib/cgraph/write.c). Recurses the subgraph tree
306
- * (write_subgs/write_body), scoping each node/edge to the subgraph(s) it
307
- * belongs to via preorder numbers (write_node_test/write_edge_test), and
308
- * re-emits an object bare (no attrs) on any scope after the first
309
- * (attrs_written). This reproduces native's per-subgraph edge re-declarations
310
- * — e.g. an edge in a rank=same subgraph is drawn once, then re-declared bare.
311
- * @see lib/cgraph/write.c:agwrite
312
- */
313
- private serialize;
314
- /** Preorder-number the subgraph tree. @see write.c:subgdfs */
315
- private subgdfs;
316
- /** A subgraph is anonymous when its name is empty or a `%N` local name. */
317
- private isAnonymous;
318
- /** A graph carries a `bb` attribute when it is the root or has a layout bb
319
- * (clusters, and any subgraph output.c computed a box for). Native seeds `bb`
320
- * on every graph via safe_dcl, so the value differs (set vs empty "") between
321
- * a boxed graph and an unboxed child — the driver that makes an anon subgraph
322
- * under the root or a cluster "relevant". @see lib/common/output.c:safe_dcl */
323
- private hasBb;
324
- /** Anonymous subgraph with no own node/edge defaults and no graph attrs
325
- * differing from its parent → inlined into the parent. Native compares every
326
- * graph attr over the root attr dict; the load-bearing ones are `bb` (set on
327
- * the parent, empty on the child) and `rank`. @see write.c:irrelevant_subgraph */
328
- private irrelevantSubgraph;
329
- /** Non-draw graph attrs a subgraph emits (rank for rank=same; clusters use
330
- * clusterAttrs). Only comparator-relevant fields need be exact.
331
- *
332
- * `rec_attach_bb` walks the ROOT and then `GD_clust` recursively, so a
333
- * subgraph the layout did not box is attached NEITHER `bb` NOR the label
334
- * triple — both keep the INPUT's values, which agwrite echoes. circo and
335
- * twopi lay out no clusters at all (`GD_clust` empty — mirrored here by
336
- * `this.clusters`, filled from endCluster), so on a re-fed dot output their
337
- * `cluster0` comes back carrying the *previous* run's `bb` and `lp`.
338
- * @see lib/common/output.c:249 rec_attach_bb */
339
- private subgGraphAttrs;
340
- private indent;
341
- /** @see write.c:write_hdr */
342
- private writeHdr;
343
- /** @see write.c:write_trl */
344
- private writeTrl;
345
- /** @see write.c:write_subgs */
346
- private writeSubgs;
347
- /** @see write.c:write_body — subgraphs, then this scope's nodes and edges. */
348
- private writeBody;
349
- /** @see write.c:write_node_test — every xdot node carries pos/size, so it is
350
- * never "default"; write it in the first scope that has not yet emitted it.
351
- * Cluster nodes (fdp compound proxies, ND_clustnode) are never declared —
352
- * C's write_plain/writenodeandport suppress them. @see lib/common/output.c:146 */
353
- private writeNodeTest;
354
- /** Emitted node name: a cluster node's synthetic `__i:<cluster>` id is written
355
- * as the cluster name it stands for. @see lib/common/output.c:114 */
356
- private emitNodeName;
357
- /** @see write.c:write_edge_test */
358
- private writeEdgeTest;
359
- /** @see write.c:write_node */
360
- private writeNode;
361
- /** @see write.c:write_edge — attrs only on first emission, bare thereafter. */
362
- private writeEdge;
363
- /** Root-graph attribute block: `_draw_`, `_ldraw_`, `bb`, `xdotversion`. */
364
- private graphAttrs;
365
- /** Cluster attribute block. C's xdot_end_cluster ALWAYS agsets `_draw_` when
366
- * the graph has clusters (even empty, e.g. peripheries=0), so emit it
367
- * unconditionally; `_ldraw_` only when the cluster has a label.
368
- * @see plugin/core/gvrender_core_dot.c:284 xdot_end_cluster */
369
- private clusterAttrs;
370
- /** Node attribute block: pos/width/height plus `_draw_`/`_ldraw_`. */
371
- private nodeAttrs;
372
- /** The `[...]` attribute body for an edge (draw ops + spline `pos`), or ''. */
373
- private edgeAttrStr;
131
+ }
132
+ /**
133
+ * DOT format renderer (`-Tdot`) — the same engine as {@link XdotRenderer} with
134
+ * the xdot draw-attribute step switched off, mirroring C's one renderer with a
135
+ * `job->render.id` switch: FORMAT_DOT is `attach_attrs(g)` then `agwrite(g)`,
136
+ * FORMAT_XDOT is `attach_attrs_and_arrows` + the `xdot_*` draw attributes then
137
+ * *the same* `agwrite(g)`. Sharing the serializer is what makes `-Tdot` emit the
138
+ * subgraph tree, per-scope node/edge scoping, and the `node [label="\N"]`
139
+ * default line, none of which a streaming emitter can produce (the whole graph
140
+ * must be walked before the first byte).
141
+ *
142
+ * The drawing callbacks are overridden to no-ops: `-Tdot` renders no draw ops,
143
+ * so accumulating them would be dead work.
144
+ *
145
+ * @see plugin/core/gvrender_core_dot.c:398 dot_begin_graph / :464 dot_end_graph
146
+ */
147
+ export declare class DotRenderer extends XdotRenderer {
148
+ readonly type: string;
149
+ protected readonly emitDraws: boolean;
150
+ textspan(_pos: Point, _span: TextSpan, _job: RenderJob): void;
151
+ ellipse(_c: Point, _rx: number, _ry: number, _f: boolean, _j: RenderJob): void;
152
+ polygon(_pts: Point[], _filled: boolean, _job: RenderJob): void;
153
+ bezier(_pts: Point[], _filled: boolean, _job: RenderJob): void;
154
+ polyline(_pts: Point[], _job: RenderJob): void;
374
155
  }
375
156
  /** @see plugin/core/gvrender_core_dot.c FORMAT_DOT */
376
157
  export declare function createDotRenderer(): RendererPlugin;