@knowvah/dot-engine 2.6.11 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api.js +18 -4
- package/dist/api.js.map +3 -3
- package/dist/async/render-async.d.ts.map +1 -1
- package/dist/common/agerr.d.ts +37 -0
- package/dist/common/agerr.d.ts.map +1 -0
- package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
- package/dist/common/color.d.ts.map +1 -1
- package/dist/common/crand.d.ts +0 -20
- package/dist/common/crand.d.ts.map +1 -1
- package/dist/common/emit-once.d.ts +6 -0
- package/dist/common/emit-once.d.ts.map +1 -0
- package/dist/common/graph-init.d.ts.map +1 -1
- package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
- package/dist/common/htmltable-emit-fill.d.ts +29 -2
- package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
- package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
- package/dist/common/htmltable-emit.d.ts.map +1 -1
- package/dist/common/htmltable-lex-expat.d.ts +1 -6
- package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
- package/dist/common/htmltable-pos-runs.d.ts +7 -4
- package/dist/common/htmltable-pos-runs.d.ts.map +1 -1
- package/dist/common/htmltable.d.ts +1 -7
- package/dist/common/htmltable.d.ts.map +1 -1
- package/dist/common/libm-trig.d.ts +5 -0
- package/dist/common/libm-trig.d.ts.map +1 -0
- package/dist/common/multicolor.d.ts +21 -13
- package/dist/common/multicolor.d.ts.map +1 -1
- package/dist/common/poly-gencode.d.ts +1 -2
- package/dist/common/poly-gencode.d.ts.map +1 -1
- package/dist/common/poly-init.d.ts.map +1 -1
- package/dist/common/postproc.d.ts.map +1 -1
- package/dist/common/random.d.ts.map +1 -1
- package/dist/common/render-state.d.ts +8 -0
- package/dist/common/render-state.d.ts.map +1 -0
- package/dist/common/splines-clip.d.ts +7 -0
- package/dist/common/splines-clip.d.ts.map +1 -1
- package/dist/common/splines-geom.d.ts +8 -0
- package/dist/common/splines-geom.d.ts.map +1 -1
- package/dist/common/splines-routespl.d.ts.map +1 -1
- package/dist/common/textmeasure-lookup.d.ts.map +1 -1
- package/dist/common/textmeasure.d.ts.map +1 -1
- package/dist/gvc/anchor.d.ts.map +1 -1
- package/dist/gvc/context.d.ts +6 -0
- package/dist/gvc/context.d.ts.map +1 -1
- package/dist/gvc/device-cluster.d.ts +15 -0
- package/dist/gvc/device-cluster.d.ts.map +1 -1
- package/dist/gvc/device-labels.d.ts +14 -0
- package/dist/gvc/device-labels.d.ts.map +1 -1
- package/dist/gvc/device.d.ts.map +1 -1
- package/dist/gvc/first-layer.d.ts +9 -0
- package/dist/gvc/first-layer.d.ts.map +1 -0
- package/dist/gvc/gvrender-set-style.d.ts +28 -0
- package/dist/gvc/gvrender-set-style.d.ts.map +1 -0
- package/dist/gvc/usershape.d.ts +18 -0
- package/dist/gvc/usershape.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3169 -1296
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/cluster.d.ts.map +1 -1
- package/dist/layout/dot/compound.d.ts.map +1 -1
- package/dist/layout/dot/conc.d.ts.map +1 -1
- package/dist/layout/dot/decomp.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-clip.d.ts +32 -14
- package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-routing.d.ts.map +1 -1
- package/dist/layout/dot/mincross-build.d.ts.map +1 -1
- package/dist/layout/dot/mincross-cross.d.ts.map +1 -1
- package/dist/layout/dot/rank.d.ts.map +1 -1
- package/dist/layout/dot/splines.d.ts.map +1 -1
- package/dist/layout/fdp/index.d.ts.map +1 -1
- package/dist/layout/fdp/tlayout-parms.d.ts.map +1 -1
- package/dist/layout/fdp/xlayout.d.ts.map +1 -1
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts +11 -0
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts +36 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts +32 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts.map +1 -0
- package/dist/layout/neato/cdt-surface.d.ts.map +1 -1
- package/dist/layout/neato/compute-hierarchy.d.ts +24 -0
- package/dist/layout/neato/compute-hierarchy.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts +20 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad.d.ts +19 -0
- package/dist/layout/neato/conjgrad.d.ts.map +1 -1
- package/dist/layout/neato/constrained-majorization.d.ts +26 -0
- package/dist/layout/neato/constrained-majorization.d.ts.map +1 -0
- package/dist/layout/neato/dijkstra.d.ts +33 -16
- package/dist/layout/neato/dijkstra.d.ts.map +1 -1
- package/dist/layout/neato/edge-len.d.ts.map +1 -1
- package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
- package/dist/layout/neato/gts-triangle.d.ts +14 -0
- package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
- package/dist/layout/neato/init.d.ts +6 -22
- package/dist/layout/neato/init.d.ts.map +1 -1
- package/dist/layout/neato/kk.d.ts.map +1 -1
- package/dist/layout/neato/kkutils.d.ts +20 -0
- package/dist/layout/neato/kkutils.d.ts.map +1 -0
- package/dist/layout/neato/make-graph-data.d.ts +43 -0
- package/dist/layout/neato/make-graph-data.d.ts.map +1 -0
- package/dist/layout/neato/matrix-ops.d.ts +46 -0
- package/dist/layout/neato/matrix-ops.d.ts.map +1 -1
- package/dist/layout/neato/multispline-router.d.ts.map +1 -1
- package/dist/layout/neato/multispline.d.ts.map +1 -1
- package/dist/layout/neato/opt-arrangement.d.ts +20 -0
- package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
- package/dist/layout/neato/poly.d.ts.map +1 -1
- package/dist/layout/neato/quad-prog-solve.d.ts +44 -0
- package/dist/layout/neato/quad-prog-solve.d.ts.map +1 -0
- package/dist/layout/neato/sgd.d.ts.map +1 -1
- package/dist/layout/neato/smart-ini-x.d.ts +9 -0
- package/dist/layout/neato/smart-ini-x.d.ts.map +1 -0
- package/dist/layout/neato/splines.d.ts +1 -1
- package/dist/layout/neato/splines.d.ts.map +1 -1
- package/dist/layout/neato/start.d.ts +4 -1
- package/dist/layout/neato/start.d.ts.map +1 -1
- package/dist/layout/neato/stress.d.ts +8 -0
- package/dist/layout/neato/stress.d.ts.map +1 -1
- package/dist/model/edge.d.ts +1 -1
- package/dist/model/edge.d.ts.map +1 -1
- package/dist/model/lazy-attrs.d.ts +10 -0
- package/dist/model/lazy-attrs.d.ts.map +1 -0
- package/dist/model/node.d.ts +1 -1
- package/dist/model/node.d.ts.map +1 -1
- package/dist/ortho/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/render/color-resolve.d.ts +49 -1
- package/dist/render/color-resolve.d.ts.map +1 -1
- package/dist/render/dot/edge-draw.d.ts +1 -1
- package/dist/render/dot/edge-draw.d.ts.map +1 -1
- package/dist/render/public.d.ts.map +1 -1
- package/dist/render/svg-edge-split.d.ts +2 -0
- package/dist/render/svg-edge-split.d.ts.map +1 -1
- package/dist/render/svg-multicolor.d.ts +2 -2
- package/dist/render/svg-multicolor.d.ts.map +1 -1
- package/dist/render/xdot-public.d.ts.map +1 -1
- package/dist/render.js +2799 -927
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/async/render-async.ts +6 -0
- package/src/common/agerr.ts +96 -0
- package/src/common/arrows-shapes-poly.ts +19 -5
- package/src/common/color.ts +38 -10
- package/src/common/crand.ts +8 -2
- package/src/common/emit-once.ts +27 -0
- package/src/common/graph-init.ts +2 -1
- package/src/common/htmltable-attr-warn.ts +3 -4
- package/src/common/htmltable-emit-fill.ts +104 -11
- package/src/common/htmltable-emit-rules.ts +2 -0
- package/src/common/htmltable-emit.ts +4 -3
- package/src/common/htmltable-lex-expat.ts +96 -23
- package/src/common/htmltable-pos-runs.ts +11 -6
- package/src/common/htmltable.ts +28 -117
- package/src/common/libm-trig.ts +43 -0
- package/src/common/multicolor.ts +88 -24
- package/src/common/poly-gencode.ts +30 -10
- package/src/common/poly-init.ts +20 -1
- package/src/common/postproc.ts +4 -1
- package/src/common/random.ts +11 -1
- package/src/common/render-state.ts +36 -0
- package/src/common/splines-clip.ts +15 -12
- package/src/common/splines-geom.ts +8 -4
- package/src/common/splines-routespl.ts +2 -1
- package/src/common/textmeasure-lookup.ts +9 -2
- package/src/common/textmeasure.ts +45 -2
- package/src/gvc/anchor.ts +1 -12
- package/src/gvc/context.ts +8 -0
- package/src/gvc/device-cluster.ts +93 -10
- package/src/gvc/device-labels.ts +27 -0
- package/src/gvc/device.ts +34 -8
- package/src/gvc/first-layer.ts +26 -0
- package/src/gvc/gvrender-set-style.ts +114 -0
- package/src/gvc/usershape.ts +26 -0
- package/src/index.ts +3 -0
- package/src/layout/dot/cluster.ts +2 -1
- package/src/layout/dot/compound.ts +3 -4
- package/src/layout/dot/conc.ts +6 -5
- package/src/layout/dot/decomp.ts +3 -0
- package/src/layout/dot/edge-route-clip.ts +46 -28
- package/src/layout/dot/edge-route-routing.ts +11 -3
- package/src/layout/dot/fastgr.ts +2 -2
- package/src/layout/dot/mincross-build.ts +2 -0
- package/src/layout/dot/mincross-cross.ts +2 -0
- package/src/layout/dot/rank.ts +2 -0
- package/src/layout/dot/splines.ts +2 -1
- package/src/layout/fdp/index.ts +2 -1
- package/src/layout/fdp/tlayout-parms.ts +9 -2
- package/src/layout/fdp/xlayout.ts +10 -2
- package/src/layout/neato/__fixtures__/dijkstra-f.ts +17 -0
- package/src/layout/neato/__fixtures__/hier/c1554.gv +33 -0
- package/src/layout/neato/__fixtures__/hier/c1554.plain +26 -0
- package/src/layout/neato/__fixtures__/hier/c1554.xdot +161 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.plain +11 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.plain +16 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.xdot +62 -0
- package/src/layout/neato/__fixtures__/hier-imds.ts +151 -0
- package/src/layout/neato/__fixtures__/hier-majorization.ts +145 -0
- package/src/layout/neato/cdt-surface.ts +12 -22
- package/src/layout/neato/compute-hierarchy.ts +90 -0
- package/src/layout/neato/conjgrad-mkernel.ts +141 -0
- package/src/layout/neato/conjgrad.ts +208 -0
- package/src/layout/neato/constrained-majorization.ts +479 -0
- package/src/layout/neato/dijkstra.ts +218 -92
- package/src/layout/neato/edge-len.ts +2 -1
- package/src/layout/neato/fdp-adjust.ts +3 -2
- package/src/layout/neato/gts-triangle.ts +42 -0
- package/src/layout/neato/init.ts +68 -68
- package/src/layout/neato/kk.ts +2 -1
- package/src/layout/neato/kkutils.ts +96 -0
- package/src/layout/neato/make-graph-data.ts +166 -0
- package/src/layout/neato/matrix-ops.ts +138 -0
- package/src/layout/neato/multispline-router.ts +3 -3
- package/src/layout/neato/multispline.ts +3 -2
- package/src/layout/neato/opt-arrangement.ts +86 -0
- package/src/layout/neato/poly.ts +3 -2
- package/src/layout/neato/quad-prog-solve.ts +489 -0
- package/src/layout/neato/sgd.ts +5 -2
- package/src/layout/neato/smart-ini-x.ts +503 -0
- package/src/layout/neato/splines.ts +4 -4
- package/src/layout/neato/start.ts +19 -14
- package/src/layout/neato/stress.ts +28 -0
- package/src/model/edge.ts +3 -2
- package/src/model/lazy-attrs.ts +36 -0
- package/src/model/node.ts +3 -2
- package/src/ortho/index.ts +2 -1
- package/src/parser/dot.js +4 -1
- package/src/parser/dot.pegjs +5 -1
- package/src/parser/index.ts +4 -3
- package/src/render/color-resolve.ts +193 -4
- package/src/render/dot/edge-draw.ts +6 -3
- package/src/render/dot.ts +1 -1
- package/src/render/public.ts +2 -0
- package/src/render/svg-edge-split.ts +6 -4
- package/src/render/svg-multicolor.ts +4 -3
- package/src/render/svg-parallel-edge.ts +2 -2
- package/src/render/xdot-public.ts +2 -0
|
@@ -22,6 +22,7 @@ import { FillType } from '../gvc/context.js';
|
|
|
22
22
|
import { emitHtmlLabel } from './htmltable-emit.js';
|
|
23
23
|
import { transformPoint } from '../gvc/device.js';
|
|
24
24
|
import { nodeAttr } from './poly-init.js';
|
|
25
|
+
import { gvrenderSetStyle, checkStyleTokens } from '../gvc/gvrender-set-style.js';
|
|
25
26
|
import { substObjAnchor, preprocessTooltip } from './subst.js';
|
|
26
27
|
import type { ResolvedFill, PolyStyleFlags } from './style-resolve.js';
|
|
27
28
|
import {
|
|
@@ -33,7 +34,8 @@ import {
|
|
|
33
34
|
styleTokens,
|
|
34
35
|
} from './style-resolve.js';
|
|
35
36
|
import { stripedBox, wedgedEllipse } from '../render/svg-multicolor.js';
|
|
36
|
-
import { resolveRenderColor } from '../render/color-resolve.js';
|
|
37
|
+
import { resolveRenderColor, reportColorNames } from '../render/color-resolve.js';
|
|
38
|
+
import { htmlLabelColors } from './htmltable-emit-fill.js';
|
|
37
39
|
import { svgNodeId } from '../render/svg-id.js';
|
|
38
40
|
import { drawRoundCorners, mcircleHack, underlineDraw } from './poly-shapes.js';
|
|
39
41
|
|
|
@@ -95,6 +97,7 @@ interface RingCtx {
|
|
|
95
97
|
job: RenderJob;
|
|
96
98
|
style: PolyStyleFlags;
|
|
97
99
|
fillcolor: string;
|
|
100
|
+
nodeName: string; // agnameof(n): the `in node %s` AGPREV of a parseSegs rv > 1
|
|
98
101
|
shape: number;
|
|
99
102
|
diagonals: boolean;
|
|
100
103
|
rounded: boolean;
|
|
@@ -113,7 +116,7 @@ function renderEllipseRing(
|
|
|
113
116
|
const pf = ring.map(
|
|
114
117
|
(v) => transformPoint({ x: v.x + coord.x, y: v.y + coord.y }, ctx.job),
|
|
115
118
|
);
|
|
116
|
-
wedgedEllipse(ctx.job, pf, ctx.fillcolor, ctx.renderer);
|
|
119
|
+
wedgedEllipse(ctx.job, pf, ctx.fillcolor, ctx.renderer, () => `in node ${ctx.nodeName}`);
|
|
117
120
|
renderEllipse(ring, coord, ctx.renderer, ctx.job, false); // boundary unfilled
|
|
118
121
|
} else {
|
|
119
122
|
renderEllipse(ring, coord, ctx.renderer, ctx.job, filled);
|
|
@@ -123,14 +126,12 @@ function renderEllipseRing(
|
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
/**
|
|
126
|
-
*
|
|
127
|
-
* style.striped + j==0.
|
|
129
|
+
* Striped j===0 ring: the multicolor box plus an unfilled boundary polygon.
|
|
128
130
|
* @see lib/common/shapes.c:poly_gencode :3037-3043
|
|
129
131
|
*/
|
|
130
|
-
/** Striped j===0 ring: the multicolor box plus an unfilled boundary polygon. */
|
|
131
132
|
function renderStripedRing(ring: Point[], coord: Point, ctx: RingCtx): void {
|
|
132
133
|
const af = ring.map((v) => transformPoint({ x: v.x + coord.x, y: v.y + coord.y }, ctx.job));
|
|
133
|
-
stripedBox(ctx.job, af, ctx.fillcolor, true, ctx.renderer);
|
|
134
|
+
stripedBox(ctx.job, af, ctx.fillcolor, true, ctx.renderer, () => `in node ${ctx.nodeName}`);
|
|
134
135
|
renderPolygon(ring, coord, ctx.renderer, ctx.job, false); // boundary unfilled
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -191,6 +192,8 @@ export function renderLabel(
|
|
|
191
192
|
job: RenderJob,
|
|
192
193
|
): void {
|
|
193
194
|
if (label.u.kind !== 'txt' || label.u.nspans < 1) return;
|
|
195
|
+
// gvrender_set_pencolor(job, lp->fontcolor), once, before the spans.
|
|
196
|
+
reportColorNames([label.fontcolor]);
|
|
194
197
|
label.pos = coord;
|
|
195
198
|
label.set = true;
|
|
196
199
|
// First-span baseline per valign (labelloc). @see lib/common/labels.c:240-251
|
|
@@ -227,6 +230,7 @@ function renderNodeLabel(
|
|
|
227
230
|
// Route the HTML label's box/fill/text ops into the node LABEL emit-state
|
|
228
231
|
// (→ _ldraw_ for xdot), not the node DRAW state. SVG ignores emit_state.
|
|
229
232
|
const html = label.u.html as PlacedHtml;
|
|
233
|
+
reportColorNames(htmlLabelColors(html));
|
|
230
234
|
withLabelEmitState(job, () => emitHtmlLabel(html, coord, renderer, job));
|
|
231
235
|
} else {
|
|
232
236
|
renderLabel(label, coord, renderer, job);
|
|
@@ -363,12 +367,27 @@ function applyPenState(obj: ObjState, styleAttr: string | undefined,
|
|
|
363
367
|
export function applyNodeStyle(obj: ObjState, n: Node, job: RenderJob): boolean {
|
|
364
368
|
const styleAttr = nodeAttr(n, n.root, 'style');
|
|
365
369
|
const colorAttr = nodeAttr(n, n.root, 'color');
|
|
370
|
+
// stylenode: gvrender_set_style(job, checkStyle list), before any colour is
|
|
371
|
+
// resolved; point_gencode passes its own fixed list instead (never unknown).
|
|
372
|
+
// @see lib/common/shapes.c:536 stylenode · :3257 point_gencode
|
|
373
|
+
if (!isPointNode(n)) {
|
|
374
|
+
const shape = n.info.shape as ShapeDesc | undefined;
|
|
375
|
+
gvrenderSetStyle(checkStyleTokens(styleTokens(styleAttr), shape?.polygon ?? null));
|
|
376
|
+
}
|
|
366
377
|
// The node's `colorscheme` window is NOT opened here: C opens it once around
|
|
367
378
|
// the whole node emission (emit_begin_node..emit_end_node), which covers both
|
|
368
379
|
// this color block and the label textspans. Both callers of this function are
|
|
369
380
|
// codefns, so they already run inside that window. @see gvc/device.ts
|
|
381
|
+
const penwidth = nodeAttr(n, n.root, 'penwidth');
|
|
382
|
+
// record_gencode resolves the pen (penColor) before the fill; poly_gencode
|
|
383
|
+
// and point_gencode resolve the fill first. Only the order in which an
|
|
384
|
+
// unknown colour is reported differs. @see shapes.c:3890-3892 vs 2985-3004
|
|
385
|
+
if ((n.info.shape as ShapeDesc | undefined)?.kind === ShapeKind.SH_RECORD) {
|
|
386
|
+
applyPenState(obj, styleAttr, colorAttr, penwidth);
|
|
387
|
+
return applyFillState(obj, n, job);
|
|
388
|
+
}
|
|
370
389
|
const filled = applyFillState(obj, n, job);
|
|
371
|
-
applyPenState(obj, styleAttr, colorAttr,
|
|
390
|
+
applyPenState(obj, styleAttr, colorAttr, penwidth);
|
|
372
391
|
return filled;
|
|
373
392
|
}
|
|
374
393
|
|
|
@@ -379,7 +398,8 @@ export function applyNodeStyle(obj: ObjState, n: Node, job: RenderJob): boolean
|
|
|
379
398
|
*/
|
|
380
399
|
function prepareDrawPoly(poly: PolygonT, filled: boolean, obj: ObjState): PolygonT {
|
|
381
400
|
if (poly.peripheries !== 0 || !filled) return poly;
|
|
382
|
-
|
|
401
|
+
// gvrender_set_pencolor resolves (and may report) the name like any other.
|
|
402
|
+
obj.penColor = resolveRenderColor('transparent');
|
|
383
403
|
return { ...poly, peripheries: 1 };
|
|
384
404
|
}
|
|
385
405
|
|
|
@@ -434,6 +454,7 @@ function resolveNodeDrawCtx(job: RenderJob, n: Node): NodeDrawCtx | null {
|
|
|
434
454
|
job,
|
|
435
455
|
style: styleFlags,
|
|
436
456
|
fillcolor: nodeAttr(n, n.root, 'fillcolor') ?? '',
|
|
457
|
+
nodeName: n.name,
|
|
437
458
|
...specialCornerFlags(drawPoly.option, styleFlags),
|
|
438
459
|
};
|
|
439
460
|
return { poly: drawPoly, coord, filled, ringCtx };
|
|
@@ -455,8 +476,7 @@ function specialCornerFlags(
|
|
|
455
476
|
}
|
|
456
477
|
|
|
457
478
|
/**
|
|
458
|
-
* Render node shape (polygon/ellipse) and label.
|
|
459
|
-
* Shape codefn — called from walkNodes.
|
|
479
|
+
* Render node shape (polygon/ellipse) and label; shape codefn, from walkNodes.
|
|
460
480
|
* @see lib/common/shapes.c:poly_gencode
|
|
461
481
|
*/
|
|
462
482
|
export function polyGencode(rawJob: unknown, rawNode: unknown): void {
|
package/src/common/poly-init.ts
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
DEFAULT_COLOR,
|
|
25
25
|
} from './make-label.js';
|
|
26
26
|
import { computeVertices } from './poly-vertices.js';
|
|
27
|
+
import { agwarningf } from './agerr.js';
|
|
28
|
+
import { imageIsSupplied, imageOpenWarning } from '../gvc/usershape.js';
|
|
27
29
|
|
|
28
30
|
// Default half-dimensions in points (72 dpi, 0.75 in wide, 0.5 in tall)
|
|
29
31
|
const DEFAULT_LW = 27;
|
|
@@ -110,6 +112,22 @@ export function buildNodeLabel(n: Node, g: Graph, measurer: TextMeasurer): void
|
|
|
110
112
|
n.info.label = makeAnyLabel(content, isHtml, font, measurer, n);
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
/**
|
|
116
|
+
* poly_init's `image` branch (a non-custom shape): when the caller supplies no
|
|
117
|
+
* image, gvusershape_size fails and C warns after gvusershape_open's own
|
|
118
|
+
* warning. The port never sizes node images.
|
|
119
|
+
* @see lib/common/shapes.c:2043-2046
|
|
120
|
+
*/
|
|
121
|
+
function warnMissingImage(n: Node, g: Graph, shapefile: string | undefined): void {
|
|
122
|
+
if (shapefile !== undefined && shapefile !== '' && nodeAttr(n, g, 'shape') !== 'epsf') return;
|
|
123
|
+
const image = nodeAttr(n, g, 'image');
|
|
124
|
+
if (image === undefined || image === '') return;
|
|
125
|
+
const gvc = g.root.info.gvc;
|
|
126
|
+
if (imageIsSupplied(image, gvc?.imageSizer, gvc?.imageResolver)) return;
|
|
127
|
+
agwarningf(imageOpenWarning(image));
|
|
128
|
+
agwarningf(`No or improper image="${image}" for node "${n.name}"`);
|
|
129
|
+
}
|
|
130
|
+
|
|
113
131
|
export function polyInit(n: Node, g: Graph, measurer: TextMeasurer): void {
|
|
114
132
|
const shapeName = nodeAttr(n, g, 'shape') ?? 'ellipse';
|
|
115
133
|
const shapefile = nodeAttr(n, g, 'shapefile');
|
|
@@ -117,12 +135,13 @@ export function polyInit(n: Node, g: Graph, measurer: TextMeasurer): void {
|
|
|
117
135
|
// C warns when the custom shape's image cannot be sized (headless: always).
|
|
118
136
|
// @see lib/common/shapes.c:2027-2033
|
|
119
137
|
if (shapefile !== undefined && shapefile !== '' && shapeName !== 'epsf') {
|
|
120
|
-
|
|
138
|
+
agwarningf(`No or improper shapefile="${shapefile}" for node "${n.name}"`);
|
|
121
139
|
}
|
|
122
140
|
if ((n.info.shape as ShapeDesc).kind === ShapeKind.SH_RECORD) {
|
|
123
141
|
if (n.info.shape_info === undefined) recordNodeInit(n, g, measurer);
|
|
124
142
|
return;
|
|
125
143
|
}
|
|
144
|
+
warnMissingImage(n, g, shapefile);
|
|
126
145
|
buildNodeLabel(n, g, measurer);
|
|
127
146
|
|
|
128
147
|
// Layout-time common_init_node already installed the attr-resolved
|
package/src/common/postproc.ts
CHANGED
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
RANKDIR_TB, RANKDIR_LR, RANKDIR_BT, RANKDIR_RL,
|
|
27
27
|
} from '../layout/dot/init.js';
|
|
28
28
|
import { addXLabels } from './xlabels-place.js';
|
|
29
|
+
import { registerRenderState } from './render-state.js';
|
|
30
|
+
import { agerrorf } from './agerr.js';
|
|
29
31
|
|
|
30
32
|
/** Concentrated multi-edge duplicate — never drawn. @see lib/common/const.h:IGNORED */
|
|
31
33
|
const IGNORED_EDGE = 6;
|
|
@@ -38,6 +40,7 @@ const IGNORED_EDGE = 6;
|
|
|
38
40
|
|
|
39
41
|
let Rankdir = 0;
|
|
40
42
|
let Offset: Point = { x: 0, y: 0 };
|
|
43
|
+
registerRenderState(() => { Rankdir = 0; Offset = { x: 0, y: 0 }; });
|
|
41
44
|
|
|
42
45
|
/** The bezier fields map_edge reads/writes. */
|
|
43
46
|
type MappedBezier = Pick<Bezier, 'list' | 'sflag' | 'eflag' | 'sp' | 'ep' | 'sArrowOps' | 'eArrowOps'>;
|
|
@@ -226,7 +229,7 @@ function translateDrawing(g: Graph): void {
|
|
|
226
229
|
// @see lib/common/postproc.c:map_edge ("lost %s %s edge")
|
|
227
230
|
if (e.info.spl === undefined) {
|
|
228
231
|
if (!(g.info.concentrate ?? false) && (e.info.edge_type ?? 0) !== IGNORED_EDGE) {
|
|
229
|
-
|
|
232
|
+
agerrorf(`lost ${e.tail.name} ${e.head.name} edge`);
|
|
230
233
|
}
|
|
231
234
|
continue;
|
|
232
235
|
}
|
package/src/common/random.ts
CHANGED
|
@@ -14,12 +14,22 @@
|
|
|
14
14
|
* @see lib/neatogen/neatoinit.c:checkStart (srand48 call)
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { registerRenderState } from './render-state.js';
|
|
18
|
+
|
|
17
19
|
const A = BigInt('0x5deece66d');
|
|
18
20
|
const C = BigInt('0xb');
|
|
19
21
|
const MASK48 = (1n << 48n) - 1n;
|
|
20
22
|
const TWO48 = 2 ** 48;
|
|
23
|
+
/**
|
|
24
|
+
* Unseeded generator state: libc's X0 = 0x1234ABCD330E (rand48 seed words
|
|
25
|
+
* 0x1234, 0xABCD, 0x330E); verified on the reference machine — an unseeded
|
|
26
|
+
* drand48() returns 0.39646477376027534.
|
|
27
|
+
* @see POSIX drand48(3)
|
|
28
|
+
*/
|
|
29
|
+
const UNSEEDED_STATE = 0x1234abcd330en;
|
|
21
30
|
|
|
22
|
-
let state =
|
|
31
|
+
let state = UNSEEDED_STATE;
|
|
32
|
+
registerRenderState(() => { state = UNSEEDED_STATE; });
|
|
23
33
|
|
|
24
34
|
/**
|
|
25
35
|
* Seed the generator: X = (seed << 16) | 0x330E (low 32 bits of seed).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-render scope for C process-level state (AD-4).
|
|
5
|
+
*
|
|
6
|
+
* Native Graphviz renders one graph per `dot` process, so every file static —
|
|
7
|
+
* RNG streams, sequence counters, the emit_once warning set — starts each
|
|
8
|
+
* graph at its C initial value (and gvFreeContext's emit_once_reset clears the
|
|
9
|
+
* warning set). The port's module statics outlive a render, so each module
|
|
10
|
+
* owning such state registers a reset that restores its C initial value, and
|
|
11
|
+
* every public render entry calls {@link resetRenderState} before any other
|
|
12
|
+
* work. Render output is then a function of its input alone.
|
|
13
|
+
*
|
|
14
|
+
* Not registered here: caller-installed hooks (setTextMeasurer,
|
|
15
|
+
* setImageSizer, setImageResolver) and debug hooks — they are configuration,
|
|
16
|
+
* not C process state.
|
|
17
|
+
*
|
|
18
|
+
* @see lib/gvc/gvcontext.c:gvFreeContext (emit_once_reset)
|
|
19
|
+
* @see lib/common/emit.c:emit_once_reset
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Registered resets, run in registration (module evaluation) order. */
|
|
23
|
+
const resets: Array<() => void> = [];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a reset restoring one module's C process state to its initial
|
|
27
|
+
* value. Called once, at module evaluation.
|
|
28
|
+
*/
|
|
29
|
+
export function registerRenderState(reset: () => void): void {
|
|
30
|
+
resets.push(reset);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Restore every registered module's C process state; call first per render. */
|
|
34
|
+
export function resetRenderState(): void {
|
|
35
|
+
for (const reset of resets) reset();
|
|
36
|
+
}
|
|
@@ -16,7 +16,7 @@ import type { Edge } from '../model/edge.js';
|
|
|
16
16
|
import type { Node } from '../model/node.js';
|
|
17
17
|
import { approxEqPt, bezierClip, updateBbBz, InsideContext } from './splines-geom.js';
|
|
18
18
|
import { MILLIPOINT, ARR_NONE } from './splines-constants.js';
|
|
19
|
-
import { arrowClipLength } from '../layout/dot/edge-route-clip.js';
|
|
19
|
+
import { arrowClipLength, distFused } from '../layout/dot/edge-route-clip.js';
|
|
20
20
|
import {
|
|
21
21
|
arrowDrawOpsForEnd, edgeArrowName, edgeArrowsize,
|
|
22
22
|
} from '../layout/dot/edge-route-arrow.js';
|
|
@@ -25,6 +25,14 @@ import { resolvePenWidth, parseStyleFlags } from './style-resolve.js';
|
|
|
25
25
|
/** Normal (filled triangle) arrowhead. @see lib/common/const.h:ARR_TYPE_NORM */
|
|
26
26
|
export const ARR_NORM = 1;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* DIST(p, q) as the oracle compiles it: sqrt(fma(dx, dx, dy * dy)), not
|
|
30
|
+
* Math.hypot (libgvc 16.1 `_arrowEndClip`/`_arrowStartClip`, incl. the inlined
|
|
31
|
+
* sphere_ctx inside(), and `_arrowOrthoClip`).
|
|
32
|
+
* @see lib/common/geom.h:DIST
|
|
33
|
+
*/
|
|
34
|
+
export const dist: (p: Point, q: Point) => number = distFused;
|
|
35
|
+
|
|
28
36
|
// Internal shape accessor type for host objects that may carry a shape.
|
|
29
37
|
type ShapeHost = { shape?: { fns?: { insidefn?: ((c: InsideContext, p: Point) => boolean) | null } | null } | null };
|
|
30
38
|
|
|
@@ -123,11 +131,6 @@ class SplineClipHelper {
|
|
|
123
131
|
return [sflag, eflag];
|
|
124
132
|
}
|
|
125
133
|
|
|
126
|
-
/** DIST(p, q). */
|
|
127
|
-
static dist(p: Point, q: Point): number {
|
|
128
|
-
return Math.hypot(p.x - q.x, p.y - q.y);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
134
|
/** Sphere inside-fn around `tip` with radius `r` for bezierClip. */
|
|
132
135
|
static sphereCtx(tip: Point, r: number): {
|
|
133
136
|
ctx: InsideContext;
|
|
@@ -135,7 +138,7 @@ class SplineClipHelper {
|
|
|
135
138
|
} {
|
|
136
139
|
return {
|
|
137
140
|
ctx: { nodeCoord: { x: 0, y: 0 }, rw: 0, bp: null },
|
|
138
|
-
fn: (_c: InsideContext, p: Point) =>
|
|
141
|
+
fn: (_c: InsideContext, p: Point) => dist(p, tip) <= r,
|
|
139
142
|
};
|
|
140
143
|
}
|
|
141
144
|
|
|
@@ -170,7 +173,7 @@ class SplineClipHelper {
|
|
|
170
173
|
SplineClipHelper.clipArrowsize(e), SplineClipHelper.clipPenwidth(e));
|
|
171
174
|
spl.eflag = eflag;
|
|
172
175
|
spl.ep = ps[endp + 3]!;
|
|
173
|
-
if (endp > startp &&
|
|
176
|
+
if (endp > startp && dist(ps[endp]!, ps[endp + 3]!) < elen) {
|
|
174
177
|
endp -= 3;
|
|
175
178
|
}
|
|
176
179
|
const sp: Point[] = [spl.ep, ps[endp + 2]!, ps[endp + 1]!, ps[endp]!];
|
|
@@ -198,7 +201,7 @@ class SplineClipHelper {
|
|
|
198
201
|
SplineClipHelper.clipArrowsize(e), SplineClipHelper.clipPenwidth(e));
|
|
199
202
|
spl.sflag = sflag;
|
|
200
203
|
spl.sp = ps[startp]!;
|
|
201
|
-
if (endp > startp &&
|
|
204
|
+
if (endp > startp && dist(ps[startp]!, ps[startp + 3]!) < slen) {
|
|
202
205
|
startp += 3;
|
|
203
206
|
}
|
|
204
207
|
const sp: Point[] = [ps[startp + 3]!, ps[startp + 2]!, ps[startp + 1]!, spl.sp];
|
|
@@ -293,7 +296,7 @@ class SplineClipHelper {
|
|
|
293
296
|
if (sflag && eflag && endp === startp) {
|
|
294
297
|
// two arrows on one segment
|
|
295
298
|
const p = ps[endp]!; const q = ps[endp + 3]!;
|
|
296
|
-
const d =
|
|
299
|
+
const d = dist(p, q);
|
|
297
300
|
let hlen = hlen0; let tlen = tlen0;
|
|
298
301
|
if (hlen + tlen >= d) { hlen = tlen = d / 3.0; }
|
|
299
302
|
const s = { x: p.x, y: p.y }; const t = { x: q.x, y: q.y };
|
|
@@ -315,7 +318,7 @@ class SplineClipHelper {
|
|
|
315
318
|
if (eflag) {
|
|
316
319
|
let hlen = hlen0;
|
|
317
320
|
const p = ps[endp]!; const q = ps[endp + 3]!;
|
|
318
|
-
const maxd = 0.9 *
|
|
321
|
+
const maxd = 0.9 * dist(p, q);
|
|
319
322
|
if (hlen >= maxd) hlen = maxd;
|
|
320
323
|
const r = { x: p.x, y: p.y };
|
|
321
324
|
if (p.y === q.y) { r.y = p.y; r.x = p.x < q.x ? q.x - hlen : q.x + hlen; }
|
|
@@ -326,7 +329,7 @@ class SplineClipHelper {
|
|
|
326
329
|
if (sflag) {
|
|
327
330
|
let tlen = tlen0;
|
|
328
331
|
const p = ps[startp]!; const q = ps[startp + 3]!;
|
|
329
|
-
const maxd = 0.9 *
|
|
332
|
+
const maxd = 0.9 * dist(p, q);
|
|
330
333
|
if (tlen >= maxd) tlen = maxd;
|
|
331
334
|
const r = { x: p.x, y: p.y };
|
|
332
335
|
if (p.y === q.y) { r.y = p.y; r.x = p.x < q.x ? p.x + tlen : p.x - tlen; }
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { Point, Box } from '../model/geom.js';
|
|
12
12
|
import { bezier as evalBezier } from './bezier.js';
|
|
13
|
+
import { fma } from './fma.js';
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Approximate equality
|
|
@@ -47,16 +48,19 @@ const HW = 2.0;
|
|
|
47
48
|
const SMALL = 0.0000000001;
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
* Distance from point p to line a-b, squared.
|
|
51
|
+
* Distance from point p to line a-b, squared. Native (arm64, clang
|
|
52
|
+
* -ffp-contract=on) fuses the numerator as fmadd(p.y-a.y, dx, -(dy*(p.x-a.x)))
|
|
53
|
+
* and the denominator as fmadd(dx, dx, dy*dy): libgvc _ptToLine2 at
|
|
54
|
+
* 0x21214-0x21244, identically in inlined _update_bb_bz and _approx_bezier.
|
|
51
55
|
* @see lib/common/geom.c:ptToLine2
|
|
52
56
|
*/
|
|
53
|
-
function ptToLine2(a: Point, b: Point, p: Point): number {
|
|
57
|
+
export function ptToLine2(a: Point, b: Point, p: Point): number {
|
|
54
58
|
const dx = b.x - a.x;
|
|
55
59
|
const dy = b.y - a.y;
|
|
56
|
-
let a2 = (p.y - a.y
|
|
60
|
+
let a2 = fma(p.y - a.y, dx, -(dy * (p.x - a.x)));
|
|
57
61
|
a2 *= a2; // square — ensures positive
|
|
58
62
|
if (a2 < SMALL) return 0; // avoid 0/0 problems
|
|
59
|
-
return a2 / (dx
|
|
63
|
+
return a2 / fma(dx, dx, dy * dy);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
/**
|
|
@@ -15,6 +15,7 @@ import { shortestPath, routeSpline, makePolyline } from '../pathplan/index.js';
|
|
|
15
15
|
import type { Poly } from '../pathplan/types.js';
|
|
16
16
|
import { INIT_DELTA, LOOP_TRIES, ROUTESPL_FUDGE } from './splines-constants.js';
|
|
17
17
|
import { fma } from './fma.js';
|
|
18
|
+
import { agerrorf } from './agerr.js';
|
|
18
19
|
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
20
21
|
// Stage-dump hook (diagnosis only; null in production — same pattern as
|
|
@@ -450,7 +451,7 @@ function routeSplinesInternal(pp: Path, polyline: boolean): Point[] | null {
|
|
|
450
451
|
if (pl === null) {
|
|
451
452
|
// C: agerrorf("in routesplines, Pshortestpath failed") — the caller then
|
|
452
453
|
// loses the edge. @see lib/common/routespl.c (Pshortestpath < 0 branch)
|
|
453
|
-
|
|
454
|
+
agerrorf('in routesplines, Pshortestpath failed');
|
|
454
455
|
return null;
|
|
455
456
|
}
|
|
456
457
|
emitDump('S3_POLY', dumpNames, () => ({
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
ALL_FONT_METRICS,
|
|
9
9
|
type FontFamilyData,
|
|
10
10
|
} from "./textmeasure-lut-data.js";
|
|
11
|
+
import { registerRenderState } from "./render-state.js";
|
|
12
|
+
import { agwarningf } from './agerr.js';
|
|
11
13
|
|
|
12
14
|
/** Times is always present; used as the fallback family. */
|
|
13
15
|
export const TIMES_FALLBACK: FontFamilyData = ALL_FONT_METRICS[0]!;
|
|
@@ -30,8 +32,13 @@ export function normalizeFontName(name: string): string {
|
|
|
30
32
|
return result;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Font names that have already triggered the fallback warning: C's emit_once
|
|
37
|
+
* set, emptied per render (a native run is one process; emit_once_reset).
|
|
38
|
+
* @see lib/common/emit.c:emit_once
|
|
39
|
+
*/
|
|
34
40
|
const warnedFontNames = new Set<string>();
|
|
41
|
+
registerRenderState(() => warnedFontNames.clear());
|
|
35
42
|
|
|
36
43
|
/** @see lib/common/textspan_lut.c:get_metrics_for_font_family */
|
|
37
44
|
export function getFamilyMetrics(fontName: string): FontFamilyData {
|
|
@@ -41,7 +48,7 @@ export function getFamilyMetrics(fontName: string): FontFamilyData {
|
|
|
41
48
|
}
|
|
42
49
|
if (!warnedFontNames.has(fontName)) {
|
|
43
50
|
warnedFontNames.add(fontName);
|
|
44
|
-
|
|
51
|
+
agwarningf(
|
|
45
52
|
`Warning: no hard-coded metrics for '${fontName}'. ` +
|
|
46
53
|
`Falling back to 'Times' metrics`,
|
|
47
54
|
);
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { type FontFamilyData } from "./textmeasure-lut-data.js";
|
|
11
11
|
import { getFamilyMetrics, normalizeFontName } from "./textmeasure-lookup.js";
|
|
12
12
|
import { canvasFont } from "./css-font.js";
|
|
13
|
+
import { agwarningf } from "./agerr.js";
|
|
14
|
+
import { registerRenderState } from "./render-state.js";
|
|
13
15
|
|
|
14
16
|
/** Number of hard-coded font families in the LUT. */
|
|
15
17
|
export const LUT_FAMILY_COUNT = 11;
|
|
@@ -72,12 +74,53 @@ function getVariantWidths(
|
|
|
72
74
|
// ── Character width lookup ────────────────────────────────────────────────────
|
|
73
75
|
|
|
74
76
|
const SPACE_CHAR_CODE = 32;
|
|
77
|
+
const FIRST_NON_ASCII = 128;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* C's two `static bool warning_already_reported` flags, one per warning.
|
|
81
|
+
* Once per render (AD-4), as one native process renders one graph.
|
|
82
|
+
* @see lib/common/textspan_lut.c:estimate_character_width_canonical
|
|
83
|
+
*/
|
|
84
|
+
const warned = new Set<string>();
|
|
85
|
+
const NON_ASCII_FLAG = "non-ascii";
|
|
86
|
+
const ASCII_FLAG = "ascii";
|
|
87
|
+
|
|
88
|
+
registerRenderState(() => warned.clear());
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The C format strings already begin with `Warning: `, and agwarningf adds its
|
|
92
|
+
* own prefix, so native prints `Warning: Warning: ...`. The trailing newline
|
|
93
|
+
* of the C format is the line terminator `console.warn` supplies.
|
|
94
|
+
*/
|
|
95
|
+
function warnNonAscii(character: number): void {
|
|
96
|
+
if (warned.has(NON_ASCII_FLAG)) return;
|
|
97
|
+
warned.add(NON_ASCII_FLAG);
|
|
98
|
+
agwarningf(
|
|
99
|
+
`Warning: no value for width of non-ASCII character ${character}. Falling back to width of space character`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function warnAscii(character: number): void {
|
|
104
|
+
if (warned.has(ASCII_FLAG)) return;
|
|
105
|
+
warned.add(ASCII_FLAG);
|
|
106
|
+
agwarningf(
|
|
107
|
+
`Warning: no value for width of ASCII character ${character}. Falling back to 0`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
75
110
|
|
|
76
111
|
/** @see lib/common/textspan_lut.c:estimate_character_width_canonical */
|
|
77
112
|
function charWidthUnits(widths: readonly number[], charCode: number): number {
|
|
78
|
-
|
|
113
|
+
let code = charCode;
|
|
114
|
+
if (code >= FIRST_NON_ASCII) {
|
|
115
|
+
warnNonAscii(code);
|
|
116
|
+
code = SPACE_CHAR_CODE;
|
|
117
|
+
}
|
|
79
118
|
const w = widths[code] ?? -1;
|
|
80
|
-
|
|
119
|
+
if (w < 0) {
|
|
120
|
+
warnAscii(code);
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
return w;
|
|
81
124
|
}
|
|
82
125
|
|
|
83
126
|
/**
|
package/src/gvc/anchor.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { substObjAnchor, preprocessTooltip } from '../common/subst.js';
|
|
|
21
21
|
import { parseStyleFlags } from '../common/style-resolve.js';
|
|
22
22
|
import { nodeAttr } from '../common/poly-init.js';
|
|
23
23
|
import { bezier } from '../common/bezier.js';
|
|
24
|
+
import { ptToLine2 } from '../common/splines-geom.js';
|
|
24
25
|
import type { RendererPlugin } from './context.js';
|
|
25
26
|
import { MapShape, type ObjState, type RenderJob } from './job.js';
|
|
26
27
|
|
|
@@ -231,8 +232,6 @@ export function beginAnchorIf(renderer: RendererPlugin, job: RenderJob): boolean
|
|
|
231
232
|
const DFLT_SAMPLE = 20;
|
|
232
233
|
/** HW: maximum distance from line, in points. @see lib/common/emit.c:739 */
|
|
233
234
|
const MAP_HW = 2.0;
|
|
234
|
-
/** SMALL: 0/0 guard in ptToLine2. @see lib/common/geom.c:176 */
|
|
235
|
-
const MAP_SMALL = 0.0000000001;
|
|
236
235
|
|
|
237
236
|
/**
|
|
238
237
|
* Coordinate-transform context for the imagemap device. `bb` is the unpadded
|
|
@@ -430,16 +429,6 @@ export function computeLabelRectMap(lab: TextlabelT, obj: ObjState, ctx: MapCtx)
|
|
|
430
429
|
|
|
431
430
|
// --- Edge spline hot spots (map_output_bspline) --------------------------
|
|
432
431
|
|
|
433
|
-
/** ptToLine2: squared distance from p to segment (a,b). @see lib/common/geom.c */
|
|
434
|
-
function ptToLine2(a: Point, b: Point, p: Point): number {
|
|
435
|
-
const dx = b.x - a.x;
|
|
436
|
-
const dy = b.y - a.y;
|
|
437
|
-
let a2 = (p.y - a.y) * dx - (p.x - a.x) * dy;
|
|
438
|
-
a2 *= a2;
|
|
439
|
-
if (a2 < MAP_SMALL) return 0;
|
|
440
|
-
return a2 / (dx * dx + dy * dy);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
432
|
/** check_control_points: both handles within HW of the chord. @see emit.c */
|
|
444
433
|
function checkControlPoints(cp: Point[]): boolean {
|
|
445
434
|
return ptToLine2(cp[0]!, cp[3]!, cp[1]!) < MAP_HW * MAP_HW
|
package/src/gvc/context.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type { DebugOptions } from '../debug.js';
|
|
|
26
26
|
import type { ImageSizer } from '../common/htmltable-types.js';
|
|
27
27
|
import type { ImageResolver } from './image-resolver.js';
|
|
28
28
|
import type { RenderJob } from './job.js'; // scaffold in T25; full class in T26
|
|
29
|
+
import { resetRenderState } from '../common/render-state.js';
|
|
29
30
|
import { RenderError, invalidArgType, invalidArgValue } from '../errors.js';
|
|
30
31
|
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
@@ -285,6 +286,12 @@ export class GvcContext {
|
|
|
285
286
|
* rendering happens in between and must see the layout state
|
|
286
287
|
* (e.g. cluster arrays).
|
|
287
288
|
*
|
|
289
|
+
* Contract (AD-4/AD-8): first resets the C process state (RNG streams,
|
|
290
|
+
* sequence counters, once-only warnings), so each `layout` call behaves
|
|
291
|
+
* as one native run — drive one graph per `layout`. Caller-installed hooks
|
|
292
|
+
* (setTextMeasurer, setImageSizer, setImageResolver, the per-context
|
|
293
|
+
* measurer/sizer/resolver) are configuration and are not reset.
|
|
294
|
+
*
|
|
288
295
|
* @throws TypeError (ERR_INVALID_ARG_TYPE / ERR_INVALID_ARG_VALUE) for a
|
|
289
296
|
* bad `g`, or an engine argument that is not registered
|
|
290
297
|
* @throws RenderError (UNKNOWN_LAYOUT) if the `layout` attribute names no
|
|
@@ -295,6 +302,7 @@ export class GvcContext {
|
|
|
295
302
|
* @see lib/gvc/gvlayout.c:gvLayoutJobs
|
|
296
303
|
*/
|
|
297
304
|
layout(g: Graph, engineName: EngineName): void {
|
|
305
|
+
resetRenderState(); // AD-8: idempotent under the public entries' own reset
|
|
298
306
|
const selected = this.checkLayoutArgs(g, engineName);
|
|
299
307
|
// C gvLayoutJobs: the graph's `layout` ATTRIBUTE unconditionally
|
|
300
308
|
// overrides the selected engine (-K / API choice); an unrecognized
|