@knowvah/dot-engine 1.2.4 → 1.2.6
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 +15 -0
- package/dist/common/graph-init.d.ts.map +1 -1
- package/dist/common/splines-clip.d.ts +8 -0
- package/dist/common/splines-clip.d.ts.map +1 -1
- package/dist/common/splines-routespl.d.ts +4 -0
- package/dist/common/splines-routespl.d.ts.map +1 -1
- package/dist/index.js +55 -1
- package/dist/index.js.map +2 -2
- package/dist/render.js +55 -1
- package/dist/render.js.map +2 -2
- package/package.json +2 -2
- package/src/common/graph-init.ts +14 -9
- package/src/common/splines-clip.ts +25 -0
- package/src/common/splines-routespl.ts +66 -0
package/README.md
CHANGED
|
@@ -172,6 +172,21 @@ Trade-off: the built-in model is reproducible across machines; the host-faithful
|
|
|
172
172
|
path matches the rendering font but is platform-dependent (as native graphviz is).
|
|
173
173
|
See the Text measurement guide for the full contract.
|
|
174
174
|
|
|
175
|
+
`CanvasTextMeasurer` accepts **any** 2D context that implements `font` +
|
|
176
|
+
`measureText().width` — the `canvas` package is one provider, not a requirement.
|
|
177
|
+
`@napi-rs/canvas` (prebuilt N-API binaries, no `prebuild-install`) works too:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { createCanvas } from '@napi-rs/canvas';
|
|
181
|
+
setTextMeasurer(new CanvasTextMeasurer(createCanvas(0, 0).getContext('2d') as unknown as CanvasRenderingContext2D));
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Pick the engine you **render** with: the two libraries resolve system fonts
|
|
185
|
+
differently (measured on macOS: `bold` Helvetica and Times/Courier substitution
|
|
186
|
+
diverge by 4–14% between them, while regular-weight Helvetica/Arial agree to
|
|
187
|
+
<0.2%). "Host-faithful" means measuring with the same font stack that later
|
|
188
|
+
draws the text — mixing engines reintroduces the mismatch you opted in to avoid.
|
|
189
|
+
|
|
175
190
|
## Security
|
|
176
191
|
|
|
177
192
|
**Treat rendered output as attacker-controlled markup whenever the DOT source is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-init.d.ts","sourceRoot":"","sources":["../../src/common/graph-init.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAa/C,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAQ5B;;;GAGG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAKzC,8DAA8D;AAC9D,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,SAAS,IAAI,CAAC;AAW3B;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAY5C;
|
|
1
|
+
{"version":3,"file":"graph-init.d.ts","sourceRoot":"","sources":["../../src/common/graph-init.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAa/C,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,yCAAyC;AACzC,eAAO,MAAM,UAAU,IAAI,CAAC;AAQ5B;;;GAGG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAKzC,8DAA8D;AAC9D,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,SAAS,IAAI,CAAC;AAW3B;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAY5C;AA2JD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAsG7D"}
|
|
@@ -18,4 +18,12 @@ export declare const ARR_NORM = 1;
|
|
|
18
18
|
export declare function newSpline(e: Edge, sz: number): Bezier;
|
|
19
19
|
/** @see lib/common/splines.c:clip_and_install */
|
|
20
20
|
export declare function clipAndInstall(fe: Edge, hn: Node, ps: Point[], pn: number, info: SplineInfo): void;
|
|
21
|
+
/**
|
|
22
|
+
* Stage-dump hook for the clip/install output (diagnosis only; null in
|
|
23
|
+
* production — same pattern as splines-routespl.ts:setRouteDump).
|
|
24
|
+
*/
|
|
25
|
+
type ClipDumpFn = (stage: string, tail: string, head: string, payload: unknown) => void;
|
|
26
|
+
/** Install (or clear) the clip-stage dump callback. Diagnosis only. */
|
|
27
|
+
export declare function setClipDump(fn: ClipDumpFn | null): void;
|
|
28
|
+
export {};
|
|
21
29
|
//# sourceMappingURL=splines-clip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splines-clip.d.ts","sourceRoot":"","sources":["../../src/common/splines-clip.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAO,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAS7C,gFAAgF;AAChF,eAAO,MAAM,QAAQ,IAAI,CAAC;AAia1B,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAQrD;AAMD,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"splines-clip.d.ts","sourceRoot":"","sources":["../../src/common/splines-clip.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAO,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAS7C,gFAAgF;AAChF,eAAO,MAAM,QAAQ,IAAI,CAAC;AAia1B,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAQrD;AAMD,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CA6BlG;AAED;;;GAGG;AACH,KAAK,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAGxF,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAEvD"}
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Point, Box } from '../model/geom.js';
|
|
10
10
|
import type { Path } from './types.js';
|
|
11
|
+
/** Stage payload consumer installed by a diagnosis harness. */
|
|
12
|
+
export type RouteDumpFn = (stage: string, tail: string, head: string, payload: unknown) => void;
|
|
13
|
+
/** Install (or clear) the routing stage-dump callback. Diagnosis only. */
|
|
14
|
+
export declare function setRouteDump(fn: RouteDumpFn | null): void;
|
|
11
15
|
/**
|
|
12
16
|
* Repairs minor errors in the box path (gaps, slight overlaps).
|
|
13
17
|
* Returns true on hard failure; false on success.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splines-routespl.d.ts","sourceRoot":"","sources":["../../src/common/splines-routespl.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"splines-routespl.d.ts","sourceRoot":"","sources":["../../src/common/splines-routespl.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAavC,+DAA+D;AAC/D,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KACxD,IAAI,CAAC;AAIV,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAEzD;AA2HD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAwBvE;AAqDD;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,GAAG,EAAE,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,KAAK,EAAE,EACZ,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,IAAI,CAKN;AAwOD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,IAAI,GAAG,KAAK,EAAE,GAAG,IAAI,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,IAAI,GAAG,KAAK,EAAE,GAAG,IAAI,CAEvD"}
|
package/dist/index.js
CHANGED
|
@@ -18427,7 +18427,23 @@ function clipAndInstall(fe, hn, ps, pn, info) {
|
|
|
18427
18427
|
};
|
|
18428
18428
|
SplineClipHelper.arrowClip(fe, hn, ps, bounds, newspl, info);
|
|
18429
18429
|
SplineClipHelper.copyToBezier(newspl, ps, bounds.start, bounds.end, g.info.bb);
|
|
18430
|
+
if (clipDump) {
|
|
18431
|
+
let cur = fe;
|
|
18432
|
+
while (cur.info.to_orig != null && cur.info.edge_type !== 0) cur = cur.info.to_orig;
|
|
18433
|
+
clipDump("S5_CLIP", cur.tail.name, cur.head.name, {
|
|
18434
|
+
inputPn: pn,
|
|
18435
|
+
bounds: { start: bounds.start, end: bounds.end },
|
|
18436
|
+
sflag: newspl.sflag,
|
|
18437
|
+
eflag: newspl.eflag,
|
|
18438
|
+
sp: { ...newspl.sp },
|
|
18439
|
+
ep: { ...newspl.ep },
|
|
18440
|
+
size: newspl.size,
|
|
18441
|
+
// emit reads bz.size, not list.length (memory: bezier-emit-size-not-length)
|
|
18442
|
+
list: newspl.list.slice(0, newspl.size).map((p) => ({ ...p }))
|
|
18443
|
+
});
|
|
18444
|
+
}
|
|
18430
18445
|
}
|
|
18446
|
+
var clipDump = null;
|
|
18431
18447
|
|
|
18432
18448
|
// src/common/splines-selfedge.ts
|
|
18433
18449
|
var SIDE_VERTICES = [12, 4, 6, 2, 3, 1, 9, 8];
|
|
@@ -23789,10 +23805,12 @@ function parseRatioKind(g) {
|
|
|
23789
23805
|
}
|
|
23790
23806
|
function parseRatioDrawing(g) {
|
|
23791
23807
|
const kind = parseRatioKind(g);
|
|
23792
|
-
if (kind !== "compress" && kind !== "fill") return;
|
|
23808
|
+
if (kind !== "compress" && kind !== "fill" && kind !== "value") return;
|
|
23793
23809
|
const sz = parseSizePoints(g.attrs.get("size"));
|
|
23794
23810
|
g.info.drawing = makeDrawing({
|
|
23795
23811
|
ratioKind: kind,
|
|
23812
|
+
// C: GD_drawing(g)->ratio = atof(p), set only for R_VALUE (setRatio).
|
|
23813
|
+
ratio: kind === "value" ? Number.parseFloat(g.attrs.get("ratio")) : 0,
|
|
23796
23814
|
size: sz ? { x: sz.x, y: sz.y } : { x: 0, y: 0 },
|
|
23797
23815
|
filled: sz?.filled ?? false
|
|
23798
23816
|
});
|
|
@@ -30462,6 +30480,19 @@ function obsPath(conf, p0, poly0, p1, poly1) {
|
|
|
30462
30480
|
}
|
|
30463
30481
|
|
|
30464
30482
|
// src/common/splines-routespl.ts
|
|
30483
|
+
var routeDump = null;
|
|
30484
|
+
function dumpIsVirtual(e) {
|
|
30485
|
+
return (e.info?.edge_type ?? 0) !== 0;
|
|
30486
|
+
}
|
|
30487
|
+
function dumpEdgeNames(pp) {
|
|
30488
|
+
let e = pp.data;
|
|
30489
|
+
while (e !== null && dumpIsVirtual(e)) e = e.info?.to_orig ?? null;
|
|
30490
|
+
if (e === null) return ["?", "?"];
|
|
30491
|
+
return [e.tail?.name ?? "?", e.head?.name ?? "?"];
|
|
30492
|
+
}
|
|
30493
|
+
function emitDump(stage, names, mk) {
|
|
30494
|
+
if (routeDump !== null && names !== null) routeDump(stage, names[0], names[1], mk());
|
|
30495
|
+
}
|
|
30465
30496
|
function overlap(i0, i1, j0, j1) {
|
|
30466
30497
|
if (i1 <= j0 || i0 >= j1) return 0;
|
|
30467
30498
|
if (i0 <= j0 && i1 >= j1) return i1 - i0;
|
|
@@ -30713,8 +30744,21 @@ function runLimitLoop(boxes, boxn, ps, initLlx, initUrx) {
|
|
|
30713
30744
|
}
|
|
30714
30745
|
function routeSplinesInternal(pp, polyline) {
|
|
30715
30746
|
const boxes = pp.boxes;
|
|
30747
|
+
const dumpNames = routeDump ? dumpEdgeNames(pp) : null;
|
|
30748
|
+
emitDump("S1_INPUT", dumpNames, () => ({
|
|
30749
|
+
nbox: pp.nbox,
|
|
30750
|
+
boxes: pp.boxes.slice(0, pp.nbox).map((b) => ({ ll: { ...b.ll }, ur: { ...b.ur } })),
|
|
30751
|
+
start: { p: { ...pp.start.p }, theta: pp.start.theta, constrained: pp.start.constrained },
|
|
30752
|
+
end: { p: { ...pp.end.p }, theta: pp.end.theta, constrained: pp.end.constrained }
|
|
30753
|
+
}));
|
|
30716
30754
|
if (checkPath(pp.nbox, boxes, pp)) return null;
|
|
30717
30755
|
const effectiveBoxn = pp.nbox;
|
|
30756
|
+
emitDump("S2_CHECKED", dumpNames, () => ({
|
|
30757
|
+
nbox: effectiveBoxn,
|
|
30758
|
+
boxes: boxes.slice(0, effectiveBoxn).map((b) => ({ ll: { ...b.ll }, ur: { ...b.ur } })),
|
|
30759
|
+
start: { p: { ...pp.start.p } },
|
|
30760
|
+
end: { p: { ...pp.end.p } }
|
|
30761
|
+
}));
|
|
30718
30762
|
let flip = false;
|
|
30719
30763
|
if (effectiveBoxn > 1 && boxes[0].ll.y > boxes[1].ll.y) {
|
|
30720
30764
|
flip = true;
|
|
@@ -30742,6 +30786,11 @@ function routeSplinesInternal(pp, polyline) {
|
|
|
30742
30786
|
console.warn("in routesplines, Pshortestpath failed");
|
|
30743
30787
|
return null;
|
|
30744
30788
|
}
|
|
30789
|
+
emitDump("S3_POLY", dumpNames, () => ({
|
|
30790
|
+
poly: polypoints.map((p) => ({ ...p })),
|
|
30791
|
+
eps: [{ ...eps[0] }, { ...eps[1] }],
|
|
30792
|
+
pl: pl.map((p) => ({ ...p }))
|
|
30793
|
+
}));
|
|
30745
30794
|
let ps;
|
|
30746
30795
|
if (polyline) {
|
|
30747
30796
|
ps = makePolyline(pl);
|
|
@@ -30751,6 +30800,11 @@ function routeSplinesInternal(pp, polyline) {
|
|
|
30751
30800
|
ps = routeSpline(edges, pl, evs);
|
|
30752
30801
|
if (ps.length === 0) return null;
|
|
30753
30802
|
}
|
|
30803
|
+
emitDump("S4_SPL", dumpNames, () => ({
|
|
30804
|
+
polyline,
|
|
30805
|
+
evs: polyline ? void 0 : buildConstraintVectors(pp).map((v) => ({ ...v })),
|
|
30806
|
+
ps: ps.map((p) => ({ ...p }))
|
|
30807
|
+
}));
|
|
30754
30808
|
if (isTriviallyBounded(ps)) {
|
|
30755
30809
|
applyTrivialBounds(boxes, effectiveBoxn, ps);
|
|
30756
30810
|
return ps;
|