@knowvah/dot-engine 1.2.2 → 1.2.3
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/dist/common/arrows-shapes-poly.d.ts +1 -1
- package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
- package/dist/common/arrows-shapes.d.ts +2 -2
- package/dist/common/arrows-shapes.d.ts.map +1 -1
- package/dist/index.js +34 -9
- package/dist/index.js.map +3 -3
- package/dist/layout/dot/edge-route-arrow.d.ts +8 -0
- package/dist/layout/dot/edge-route-arrow.d.ts.map +1 -1
- package/dist/model/geom.d.ts +5 -0
- package/dist/model/geom.d.ts.map +1 -1
- package/dist/render.js +54 -29
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/common/arrows-shapes-poly.ts +20 -1
- package/src/common/arrows-shapes.ts +6 -2
- package/src/layout/dot/edge-route-arrow.ts +7 -2
- package/src/model/geom.ts +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knowvah/dot-engine",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Faithful TypeScript port of Graphviz (dot, neato, fdp, sfdp, circo, twopi, osage, patchwork) — no C: no native binary, no WASM. Oracle-verified against the C implementation, runs in the browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphviz",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Point } from '../model/geom.js';
|
|
12
|
+
import { ccwrotatepf, cwrotatepf } from '../model/geom.js';
|
|
12
13
|
import type { GenResult } from './arrows-shapes-util.js';
|
|
13
14
|
import {
|
|
14
15
|
vsub, vadd, miterShape, negUnit, backwardDelta, axialProjection,
|
|
@@ -217,7 +218,25 @@ function curveControlPoints(p: Point, v: Point, w: Point, flag: number): Point[]
|
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
/** @see lib/common/arrows.c:arrow_type_curve */
|
|
220
|
-
export function genCurve(
|
|
221
|
+
export function genCurve(
|
|
222
|
+
p0: Point, u: Point, _arrowsize: number, penwidth: number, flag: number,
|
|
223
|
+
rankdir = 0,
|
|
224
|
+
): GenResult {
|
|
225
|
+
// C generates arrowheads at RENDER time, in the final (post-rankdir) frame;
|
|
226
|
+
// the port generates during routing and postproc rotates the stored ops.
|
|
227
|
+
// That round-trip is exact for every other generator, but this formula is
|
|
228
|
+
// not rotation-equivariant (0.95 shortens the x arm only; the y arm comes
|
|
229
|
+
// from AF0/AF3 verbatim), so run it in the final frame and rotate back.
|
|
230
|
+
// The rankdir maps are pure sign/swaps — rankdir 0 (TB) is untouched.
|
|
231
|
+
if (rankdir !== 0) {
|
|
232
|
+
const rot = rankdir * 90;
|
|
233
|
+
const fin = genCurve(ccwrotatepf(p0, rot), ccwrotatepf(u, rot), _arrowsize, penwidth, flag);
|
|
234
|
+
return {
|
|
235
|
+
ops: fin.ops.map((op) =>
|
|
236
|
+
'points' in op ? { ...op, points: op.points.map((pt) => cwrotatepf(pt, rot)) } : op),
|
|
237
|
+
q: cwrotatepf(fin.q, rot),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
221
240
|
const arrowwidth = penwidth > 4 ? (0.5 * penwidth) / 4 : 0.5;
|
|
222
241
|
const noShift = (flag & ARR_MOD_INV) || (u.x === 0 && u.y === 0);
|
|
223
242
|
const p = noShift ? p0 : vsub(p0, backwardDelta(u, penwidth));
|
|
@@ -319,6 +319,7 @@ export function arrowLength(comps: ResolvedArrow[], arrowsize: number, penwidth:
|
|
|
319
319
|
*/
|
|
320
320
|
export function dispatchSimple(
|
|
321
321
|
r: ResolvedArrow, tip: Point, dir: Point, arrowsize: number, penwidth: number,
|
|
322
|
+
rankdir = 0,
|
|
322
323
|
): GenResult {
|
|
323
324
|
const flag = arrowFlag(r);
|
|
324
325
|
const u = componentU(dir, r.lenfact, arrowsize);
|
|
@@ -328,7 +329,9 @@ export function dispatchSimple(
|
|
|
328
329
|
case ARR_TYPE_BOX: return genBox(tip, u, arrowsize, penwidth, flag);
|
|
329
330
|
case ARR_TYPE_DIAMOND: return genDiamond(tip, u, arrowsize, penwidth, flag);
|
|
330
331
|
case ARR_TYPE_DOT: return genDot(tip, u, arrowsize, penwidth, flag);
|
|
331
|
-
|
|
332
|
+
// curve is the one generator that is not rotation-equivariant; it needs
|
|
333
|
+
// the rankdir to reproduce C's render-time (final-frame) generation.
|
|
334
|
+
case ARR_TYPE_CURVE: return genCurve(tip, u, arrowsize, penwidth, flag, rankdir);
|
|
332
335
|
case ARR_TYPE_GAP: return genGap(tip, u, arrowsize, penwidth, flag);
|
|
333
336
|
case ARR_TYPE_NORM:
|
|
334
337
|
default:
|
|
@@ -345,6 +348,7 @@ export function dispatchSimple(
|
|
|
345
348
|
*/
|
|
346
349
|
export function arrowDrawOps(
|
|
347
350
|
comps: ResolvedArrow[], tip: Point, dir: Point, arrowsize: number, penwidth: number,
|
|
351
|
+
rankdir = 0,
|
|
348
352
|
): ArrowDrawOp[] {
|
|
349
353
|
const ops: ArrowDrawOp[] = [];
|
|
350
354
|
let p = tip;
|
|
@@ -352,7 +356,7 @@ export function arrowDrawOps(
|
|
|
352
356
|
for (let i = 0; i < n; i++) {
|
|
353
357
|
const r = comps[i];
|
|
354
358
|
if ((r.type & ARR_TYPE_MASK) === ARR_TYPE_NONE) break;
|
|
355
|
-
const res = dispatchSimple(r, p, dir, arrowsize, penwidth);
|
|
359
|
+
const res = dispatchSimple(r, p, dir, arrowsize, penwidth, rankdir);
|
|
356
360
|
ops.push(...res.ops);
|
|
357
361
|
p = res.q;
|
|
358
362
|
}
|
|
@@ -56,7 +56,11 @@ export function arrowheadPolygon(arrowTip: Point, arrowDir: Point, penwidth = 1.
|
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
|
|
58
58
|
/** Minimal edge shape exposing the arrow-related attributes. */
|
|
59
|
-
interface ArrowAttrEdge {
|
|
59
|
+
interface ArrowAttrEdge {
|
|
60
|
+
attrs: Map<string, string>;
|
|
61
|
+
/** Present on real edges; the curve generator needs the rankdir frame. */
|
|
62
|
+
tail?: { root?: { info?: { rankdir?: number } } };
|
|
63
|
+
}
|
|
60
64
|
|
|
61
65
|
/** The arrowhead/arrowtail name for an edge end (default "normal"). */
|
|
62
66
|
export function edgeArrowName(e: ArrowAttrEdge, end: 'head' | 'tail'): string {
|
|
@@ -81,7 +85,8 @@ export function arrowDrawOpsForEnd(
|
|
|
81
85
|
e: ArrowAttrEdge, end: 'head' | 'tail', tip: Point, dir: Point, penwidth: number,
|
|
82
86
|
): ArrowDrawOp[] {
|
|
83
87
|
const comps = parseArrow(edgeArrowName(e, end)).map(resolveArrowType);
|
|
88
|
+
const rankdir = (e.tail?.root?.info?.rankdir ?? 0) & 0x3;
|
|
84
89
|
// Pass the RAW direction (not normalized): componentU reproduces arrow_gen's
|
|
85
90
|
// EPSILON normalization, which is relative to the raw shaft magnitude.
|
|
86
|
-
return arrowDrawOps(comps, tip, dir, edgeArrowsize(e), penwidth);
|
|
91
|
+
return arrowDrawOps(comps, tip, dir, edgeArrowsize(e), penwidth, rankdir);
|
|
87
92
|
}
|
package/src/model/geom.ts
CHANGED
|
@@ -144,6 +144,20 @@ export function ccwrotatepf(p: Point, ccwrot: number): Point {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Clockwise counterpart — the exact inverse of {@link ccwrotatepf} per case.
|
|
149
|
+
* @see lib/common/geom.c:cwrotatepf
|
|
150
|
+
*/
|
|
151
|
+
export function cwrotatepf(p: Point, cwrot: number): Point {
|
|
152
|
+
switch (cwrot) {
|
|
153
|
+
case 0: return p;
|
|
154
|
+
case 90: return { x: p.y, y: -p.x };
|
|
155
|
+
case 180: return { x: p.x, y: -p.y };
|
|
156
|
+
case 270: return { x: p.y, y: p.x };
|
|
157
|
+
default: return p;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
147
161
|
/**
|
|
148
162
|
* Returns true if box `b0` completely contains box `b1`.
|
|
149
163
|
*
|