@knowvah/dot-engine 2.6.11 → 2.6.12

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 (125) hide show
  1. package/dist/async/render-async.d.ts.map +1 -1
  2. package/dist/common/agerr.d.ts +37 -0
  3. package/dist/common/agerr.d.ts.map +1 -0
  4. package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
  5. package/dist/common/color.d.ts.map +1 -1
  6. package/dist/common/crand.d.ts +0 -20
  7. package/dist/common/crand.d.ts.map +1 -1
  8. package/dist/common/emit-once.d.ts +6 -0
  9. package/dist/common/emit-once.d.ts.map +1 -0
  10. package/dist/common/graph-init.d.ts.map +1 -1
  11. package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
  12. package/dist/common/htmltable-emit-fill.d.ts +9 -0
  13. package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
  14. package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
  15. package/dist/common/htmltable-lex-expat.d.ts +1 -6
  16. package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
  17. package/dist/common/htmltable.d.ts +1 -7
  18. package/dist/common/htmltable.d.ts.map +1 -1
  19. package/dist/common/poly-gencode.d.ts.map +1 -1
  20. package/dist/common/poly-init.d.ts.map +1 -1
  21. package/dist/common/postproc.d.ts.map +1 -1
  22. package/dist/common/random.d.ts.map +1 -1
  23. package/dist/common/render-state.d.ts +8 -0
  24. package/dist/common/render-state.d.ts.map +1 -0
  25. package/dist/common/splines-geom.d.ts +8 -0
  26. package/dist/common/splines-geom.d.ts.map +1 -1
  27. package/dist/common/splines-routespl.d.ts.map +1 -1
  28. package/dist/common/textmeasure-lookup.d.ts.map +1 -1
  29. package/dist/common/textmeasure.d.ts.map +1 -1
  30. package/dist/gvc/anchor.d.ts.map +1 -1
  31. package/dist/gvc/device-cluster.d.ts.map +1 -1
  32. package/dist/gvc/device-labels.d.ts +14 -0
  33. package/dist/gvc/device-labels.d.ts.map +1 -1
  34. package/dist/gvc/device.d.ts.map +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +765 -451
  37. package/dist/index.js.map +4 -4
  38. package/dist/layout/dot/cluster.d.ts.map +1 -1
  39. package/dist/layout/dot/conc.d.ts.map +1 -1
  40. package/dist/layout/dot/decomp.d.ts.map +1 -1
  41. package/dist/layout/dot/edge-route-clip.d.ts +18 -10
  42. package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
  43. package/dist/layout/dot/mincross-build.d.ts.map +1 -1
  44. package/dist/layout/dot/mincross-cross.d.ts.map +1 -1
  45. package/dist/layout/dot/rank.d.ts.map +1 -1
  46. package/dist/layout/dot/splines.d.ts.map +1 -1
  47. package/dist/layout/fdp/index.d.ts.map +1 -1
  48. package/dist/layout/fdp/tlayout-parms.d.ts.map +1 -1
  49. package/dist/layout/fdp/xlayout.d.ts.map +1 -1
  50. package/dist/layout/neato/edge-len.d.ts.map +1 -1
  51. package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
  52. package/dist/layout/neato/init.d.ts +4 -10
  53. package/dist/layout/neato/init.d.ts.map +1 -1
  54. package/dist/layout/neato/kk.d.ts.map +1 -1
  55. package/dist/layout/neato/multispline-router.d.ts.map +1 -1
  56. package/dist/layout/neato/multispline.d.ts.map +1 -1
  57. package/dist/layout/neato/poly.d.ts.map +1 -1
  58. package/dist/layout/neato/sgd.d.ts.map +1 -1
  59. package/dist/layout/neato/start.d.ts.map +1 -1
  60. package/dist/ortho/index.d.ts.map +1 -1
  61. package/dist/parser/index.d.ts.map +1 -1
  62. package/dist/render/color-resolve.d.ts +49 -1
  63. package/dist/render/color-resolve.d.ts.map +1 -1
  64. package/dist/render/public.d.ts.map +1 -1
  65. package/dist/render/svg-edge-split.d.ts +2 -0
  66. package/dist/render/svg-edge-split.d.ts.map +1 -1
  67. package/dist/render/xdot-public.d.ts.map +1 -1
  68. package/dist/render.js +513 -200
  69. package/dist/render.js.map +4 -4
  70. package/package.json +1 -1
  71. package/src/async/render-async.ts +6 -0
  72. package/src/common/agerr.ts +96 -0
  73. package/src/common/arrows-shapes-poly.ts +19 -5
  74. package/src/common/color.ts +38 -10
  75. package/src/common/crand.ts +8 -2
  76. package/src/common/emit-once.ts +27 -0
  77. package/src/common/graph-init.ts +2 -1
  78. package/src/common/htmltable-attr-warn.ts +3 -4
  79. package/src/common/htmltable-emit-fill.ts +48 -0
  80. package/src/common/htmltable-emit-rules.ts +2 -0
  81. package/src/common/htmltable-lex-expat.ts +78 -23
  82. package/src/common/htmltable.ts +15 -16
  83. package/src/common/poly-gencode.ts +14 -2
  84. package/src/common/poly-init.ts +2 -1
  85. package/src/common/postproc.ts +4 -1
  86. package/src/common/random.ts +11 -1
  87. package/src/common/render-state.ts +36 -0
  88. package/src/common/splines-geom.ts +8 -4
  89. package/src/common/splines-routespl.ts +2 -1
  90. package/src/common/textmeasure-lookup.ts +10 -3
  91. package/src/common/textmeasure.ts +45 -2
  92. package/src/gvc/anchor.ts +1 -12
  93. package/src/gvc/device-cluster.ts +37 -9
  94. package/src/gvc/device-labels.ts +27 -0
  95. package/src/gvc/device.ts +18 -6
  96. package/src/index.ts +3 -0
  97. package/src/layout/dot/cluster.ts +2 -1
  98. package/src/layout/dot/conc.ts +6 -5
  99. package/src/layout/dot/decomp.ts +3 -0
  100. package/src/layout/dot/edge-route-clip.ts +27 -21
  101. package/src/layout/dot/mincross-build.ts +2 -0
  102. package/src/layout/dot/mincross-cross.ts +2 -0
  103. package/src/layout/dot/rank.ts +2 -0
  104. package/src/layout/dot/splines.ts +2 -1
  105. package/src/layout/fdp/index.ts +2 -1
  106. package/src/layout/fdp/tlayout-parms.ts +9 -2
  107. package/src/layout/fdp/xlayout.ts +10 -2
  108. package/src/layout/neato/cdt-surface.ts +6 -6
  109. package/src/layout/neato/edge-len.ts +2 -1
  110. package/src/layout/neato/fdp-adjust.ts +3 -2
  111. package/src/layout/neato/init.ts +11 -11
  112. package/src/layout/neato/kk.ts +2 -1
  113. package/src/layout/neato/multispline-router.ts +3 -3
  114. package/src/layout/neato/multispline.ts +3 -2
  115. package/src/layout/neato/poly.ts +3 -2
  116. package/src/layout/neato/sgd.ts +5 -2
  117. package/src/layout/neato/start.ts +14 -10
  118. package/src/ortho/index.ts +3 -1
  119. package/src/parser/dot.js +4 -1
  120. package/src/parser/dot.pegjs +5 -1
  121. package/src/parser/index.ts +4 -3
  122. package/src/render/color-resolve.ts +193 -4
  123. package/src/render/public.ts +2 -0
  124. package/src/render/svg-edge-split.ts +4 -2
  125. package/src/render/xdot-public.ts +2 -0
@@ -18,10 +18,17 @@
18
18
 
19
19
  import {
20
20
  colorxlate,
21
+ ColorxlateResult,
21
22
  getColorScheme,
22
23
  setColorScheme,
23
24
  type GVColor,
24
25
  } from '../common/color.js';
26
+ import type { Edge } from '../model/edge.js';
27
+ import { registerRenderState } from '../common/render-state.js';
28
+ import { agerrorf, agwarningf } from '../common/agerr.js';
29
+ import { parseSegs } from '../common/multicolor.js';
30
+ import { findStopColor, styleTokens } from '../common/style-resolve.js';
31
+ import { emitOnce } from '../common/emit-once.js';
25
32
 
26
33
  /**
27
34
  * The SVG renderer's known color names, ported verbatim from
@@ -78,19 +85,70 @@ const SVG_KNOWN_COLORS: readonly string[] = [
78
85
  /** Lower-cased lookup set for the SVG known colors (case-insensitive, as C). */
79
86
  export const KNOWN_COLORS: ReadonlySet<string> = new Set(SVG_KNOWN_COLORS);
80
87
 
88
+ /**
89
+ * Whether the active renderer has the SVG known-colors list (svg). Only then
90
+ * does a known name skip colorxlate; every other renderer runs colorxlate on
91
+ * every name, which under a colorscheme can reject a name the list accepts.
92
+ * Per render (AD-4); a render that sets nothing is svg.
93
+ * @see lib/gvc/gvrender.c:195-199 gvrender_resolve_color (knowncolors)
94
+ */
95
+ const rendererColors = { hasKnownList: true };
96
+ registerRenderState(() => {
97
+ rendererColors.hasKnownList = true;
98
+ });
99
+
100
+ /** Select the active renderer's known-colors behaviour; svg is the only list. */
101
+ export function setRendererKnownColors(hasList: boolean): void {
102
+ rendererColors.hasKnownList = hasList;
103
+ }
104
+
105
+ /**
106
+ * The first color of a `c1:c2` list: gvrender_set_pencolor cuts the name at the
107
+ * first ':' before resolving it.
108
+ * @see lib/gvc/gvrender.c:gvrender_set_pencolor
109
+ */
110
+ export function firstColor(name: string): string {
111
+ const i = name.indexOf(':');
112
+ return i < 0 ? name : name.slice(0, i);
113
+ }
114
+
81
115
  /**
82
116
  * Resolve a raw color string to a canonical {@link GVColor} for rendering.
83
117
  *
118
+ * `report` false resolves without the unknown-color warning, for a caller that
119
+ * resolves earlier than C does (the warning belongs to C's own call order).
120
+ *
84
121
  * @see lib/gvc/gvrender.c:188-213 gvrender_resolve_color
85
122
  */
86
- export function resolveRenderColor(raw: string): GVColor {
123
+ export function resolveRenderColor(raw: string, report = true): GVColor {
87
124
  if (raw === '' || raw === 'none') return { type: 'none' };
88
125
  // C keeps a known name verbatim (case preserved); only the compare is
89
126
  // case-insensitive (bsearch over a lowercase table).
90
- if (KNOWN_COLORS.has(raw.toLowerCase())) return { type: 'string', s: raw };
127
+ const known = KNOWN_COLORS.has(raw.toLowerCase());
128
+ if (known && (rendererColors.hasKnownList || !report)) return { type: 'string', s: raw };
91
129
  const color: GVColor = { type: 'rgba', r: 0, g: 0, b: 0, a: 0 };
92
- colorxlate(raw, color, 'rgba');
93
- return color;
130
+ const rc = colorxlate(raw, color, 'rgba');
131
+ if (report) reportColorxlate(raw, rc);
132
+ return known ? { type: 'string', s: raw } : color;
133
+ }
134
+
135
+ /**
136
+ * The non-OK branch of gvrender_resolve_color: an unknown name warns once per
137
+ * render (emit_once "color <name>"), any other failure is an error.
138
+ *
139
+ * The result is renderer independent: every renderer's known-colors list is
140
+ * either absent (dot, xdot, json, cmapx, ...) or the SVG list, whose names all
141
+ * colorxlate to COLOR_OK, so only the xlate result decides whether C warns.
142
+ *
143
+ * @see lib/gvc/gvrender.c:201-209 gvrender_resolve_color
144
+ */
145
+ function reportColorxlate(name: string, rc: ColorxlateResult): void {
146
+ if (rc === ColorxlateResult.ColorOk) return;
147
+ if (rc === ColorxlateResult.ColorUnknown) {
148
+ if (emitOnce(`color ${name}`)) agwarningf(`${name} is not a known color.`);
149
+ } else {
150
+ agerrorf('error in colorxlate()');
151
+ }
94
152
  }
95
153
 
96
154
  /**
@@ -169,3 +227,134 @@ export function textFillAttrs(fontColor: string | null): string {
169
227
  if (byte < 255) out += ' fill-opacity="' + (byte / 255).toFixed(6) + '"';
170
228
  return out;
171
229
  }
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Unknown-color reporting in C's call order
233
+ //
234
+ // Which names C hands to gvrender_resolve_color, and in what order, is fixed by
235
+ // the emit code (emit_edge_graphics, emit_background, emit_label, ...), not by
236
+ // the renderer. The port's renderers resolve in their own order and not at all
237
+ // for the non-drawing ones (plain, imap, cmapx), so each pass below lists the
238
+ // names in C's order and resolves them once, before any renderer code runs;
239
+ // emit_once then makes every later resolve of the same name silent.
240
+ // ---------------------------------------------------------------------------
241
+
242
+ /** @see lib/common/const.h DEFAULT_COLOR */
243
+ const DEFAULT_COLOR = 'black';
244
+ /** @see lib/common/emit.c:452 AEQ0 (EPS) */
245
+ const SEG_EPS = 1e-5;
246
+
247
+ /**
248
+ * Resolve each name as gvrender_set_pencolor/_fillcolor/_gradient_vals would
249
+ * (cut at the first ':'), reporting unknown colors.
250
+ */
251
+ export function reportColorNames(names: readonly string[]): void {
252
+ for (const name of names) resolveRenderColor(firstColor(name));
253
+ }
254
+
255
+ /** The per-bezier arrow flags emit_edge_graphics consults. */
256
+ export interface EdgeArrowFlags {
257
+ sflag: boolean;
258
+ eflag: boolean;
259
+ }
260
+
261
+ /** The inputs of emit_edge_graphics' colour decisions. */
262
+ export interface EdgeColorSpec {
263
+ /** late_string(e, E_color, ""). */
264
+ color: string;
265
+ /** E_fillcolor when set and non-empty, else "". */
266
+ fillcolor: string;
267
+ tapered: boolean;
268
+ arrows: readonly EdgeArrowFlags[];
269
+ }
270
+
271
+ /** Count occurrences of one character. */
272
+ function countChar(s: string, ch: string): number {
273
+ return s.split(ch).length - 1;
274
+ }
275
+
276
+ /**
277
+ * The colours C's multicolor() sets, or null when the colour spec is rejected
278
+ * (parseSegs rv 1 or 2) and emit_edge_graphics falls back to DEFAULT_COLOR.
279
+ * @see lib/common/emit.c:1975 multicolor
280
+ */
281
+ function multicolorNames(
282
+ color: string,
283
+ arrows: readonly EdgeArrowFlags[],
284
+ ): string[] | null {
285
+ const { segs, error } = parseSegs(color);
286
+ if (error === 1 || error === 2) return null;
287
+ const out: string[] = [];
288
+ const front = segs[0]?.color ?? null;
289
+ for (const bz of arrows) {
290
+ let endcolor: string | null = null;
291
+ for (const seg of segs) {
292
+ if (seg.color === null) break;
293
+ if (Math.abs(seg.t) < SEG_EPS) continue;
294
+ out.push(seg.color);
295
+ endcolor = seg.color;
296
+ }
297
+ if (bz.sflag && front !== null) out.push(front);
298
+ if (bz.eflag && endcolor !== null) out.push(endcolor);
299
+ }
300
+ return out;
301
+ }
302
+
303
+ /** The tapered branch: transparent pen, colour as fill, then pen and fill. */
304
+ function taperedNames(color: string, fillcolor: string, hasFill: boolean): string[] {
305
+ const pen = color === '' ? DEFAULT_COLOR : color;
306
+ return hasFill ? [pen, fillcolor === '' ? DEFAULT_COLOR : fillcolor] : [pen];
307
+ }
308
+
309
+ /**
310
+ * The names emit_edge_graphics resolves for an edge with a spline, in order.
311
+ * @see lib/common/emit.c:2350 emit_edge_graphics
312
+ */
313
+ export function edgeGraphicsColors(spec: EdgeColorSpec): string[] {
314
+ const numc = countChar(spec.color, ':');
315
+ let color = spec.color;
316
+ if (countChar(color, ';') > 0 && numc > 0) {
317
+ const multi = multicolorNames(color, spec.arrows);
318
+ if (multi !== null) return multi;
319
+ color = DEFAULT_COLOR;
320
+ }
321
+ const hasFill = spec.fillcolor !== '';
322
+ const early = hasFill ? [spec.fillcolor] : [];
323
+ if (spec.tapered) return [...early, ...taperedNames(color, spec.fillcolor, hasFill)];
324
+ if (numc > 0) return [...early, ...color.split(':').filter((c) => c !== '')];
325
+ const fill = hasFill ? spec.fillcolor : color;
326
+ if (color !== '') return [...early, color, fill];
327
+ return [...early, DEFAULT_COLOR, fill !== '' ? fill : DEFAULT_COLOR];
328
+ }
329
+
330
+ /**
331
+ * Report the unknown colours of an edge's graphics in C's order.
332
+ * @see lib/common/emit.c:2350 emit_edge_graphics
333
+ */
334
+ export function reportEdgeColors(e: Edge): void {
335
+ const spl = e.info.spl;
336
+ if (!spl) return;
337
+ const color = e.attrs.get('color') ?? '';
338
+ reportColorNames(edgeGraphicsColors({
339
+ color,
340
+ fillcolor: e.attrs.get('fillcolor') ?? '',
341
+ tapered: styleTokens(e.attrs.get('style')).includes('tapered'),
342
+ arrows: spl.list.map((bz) => ({ sflag: bz.sflag !== 0, eflag: bz.eflag !== 0 })),
343
+ }));
344
+ }
345
+
346
+ /**
347
+ * The names emit_background resolves: the fill (a gradient's first colour, the
348
+ * transparent pen, then the stop). Only a truecolor device (svg) keeps a
349
+ * transparent background unpainted; elsewhere it becomes white.
350
+ * @see lib/common/emit.c:1476 emit_background
351
+ */
352
+ export function backgroundColors(bgcolor: string | undefined, truecolor: boolean): string[] {
353
+ let str = bgcolor !== undefined && bgcolor !== '' ? bgcolor : 'white';
354
+ if (str === 'transparent') {
355
+ if (truecolor) return [];
356
+ str = 'white';
357
+ }
358
+ const stop = findStopColor(str);
359
+ return stop === null ? [str, 'transparent'] : [stop.fillColor, 'transparent', stop.stopColor];
360
+ }
@@ -14,6 +14,7 @@ import type { Graph } from '../model/graph.js';
14
14
  import { createDefaultContext } from '../gvc/default-context.js';
15
15
  import { render as deviceRender } from '../gvc/device.js';
16
16
  import { invalidArgType, rethrowAtBoundary } from '../errors.js';
17
+ import { resetRenderState } from '../common/render-state.js';
17
18
  import type { EngineName } from '../gvc/context.js';
18
19
 
19
20
  // ---------------------------------------------------------------------------
@@ -118,6 +119,7 @@ export function render(
118
119
  opts?: RenderOptions,
119
120
  ): string {
120
121
  checkRenderArgs(g, format, opts);
122
+ resetRenderState(); // AD-4: one render = one native process
121
123
  const engine: EngineName = opts?.engine ?? 'dot';
122
124
  const inlineImages = opts?.inlineImages ?? false;
123
125
  const ctx = createDefaultContext();
@@ -20,6 +20,7 @@ import type { Edge } from '../model/edge.js';
20
20
  import type { RenderJob } from '../gvc/job.js';
21
21
  import type { Point, Bezier } from '../model/geom.js';
22
22
  import { bezier } from '../common/bezier.js';
23
+ import { dist2 } from '../common/spline-midpoint.js';
23
24
  import { parseSegs } from '../common/multicolor.js';
24
25
  import { transformPoint } from '../gvc/device.js';
25
26
  import { emitBezierPath, emitDash, emitPenWidth } from './svg-helpers.js';
@@ -31,12 +32,13 @@ const PENWIDTH_THRESHOLD = 0.005;
31
32
  /** AEQ0 tolerance for fraction comparisons. */
32
33
  const EPS = 1e-5;
33
34
 
35
+ /** DIST: sqrt of the fused DIST2 (libgvc splitBSpline: fmla + fsqrt). @see lib/common/geom.h:DIST */
34
36
  function dist(a: Point, b: Point): number {
35
- return Math.hypot(a.x - b.x, a.y - b.y);
37
+ return Math.sqrt(dist2(a, b));
36
38
  }
37
39
 
38
40
  /** Chord length of one cubic (control points at off..off+3). @see emit.c:1908 */
39
- function approxLen(list: Point[], off: number): number {
41
+ export function approxLen(list: Point[], off: number): number {
40
42
  return dist(list[off]!, list[off + 1]!) +
41
43
  dist(list[off + 1]!, list[off + 2]!) +
42
44
  dist(list[off + 2]!, list[off + 3]!);
@@ -35,6 +35,7 @@ import { createDefaultContext } from '../gvc/default-context.js';
35
35
  import { parseXDot } from '../xdot/index.js';
36
36
  import type { EngineName } from '../gvc/context.js';
37
37
  import { invalidArgType, rethrowAtBoundary } from '../errors.js';
38
+ import { resetRenderState } from '../common/render-state.js';
38
39
 
39
40
  /**
40
41
  * `Xdot` — the parsed result of one xdot attribute stream: `ops` (the
@@ -160,6 +161,7 @@ function layoutAndRenderXdot(g: Graph, engine: EngineName): string {
160
161
  */
161
162
  export function getDrawOps(g: Graph, opts?: DrawOpsOptions): XdotOp[] {
162
163
  checkDrawOpsArgs(g, opts);
164
+ resetRenderState(); // AD-4: one render = one native process
163
165
  const engine = opts?.engine ?? DEFAULT_DRAW_ENGINE;
164
166
  return collectGraphOps(parse(layoutAndRenderXdot(g, engine)));
165
167
  }