@knowvah/dot-engine 1.3.0 → 1.5.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/dist/api/geometry.d.ts +50 -0
- package/dist/api/geometry.d.ts.map +1 -1
- package/dist/api.js +24 -1
- package/dist/api.js.map +2 -2
- package/dist/index.js +24 -1
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/api/geometry.ts +82 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knowvah/dot-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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",
|
package/src/api/geometry.ts
CHANGED
|
@@ -106,13 +106,37 @@ export interface NodeGeometry {
|
|
|
106
106
|
* type) still holds calloc-zero coordinates, so it is reported as absent
|
|
107
107
|
* rather than as a label at the origin.
|
|
108
108
|
*
|
|
109
|
+
* `sp`/`ep` are the arrow attachment points. When an end carries an arrow the
|
|
110
|
+
* spline is shortened to leave room for it, and the arrow spans from the
|
|
111
|
+
* terminal control point out to this point — so a consumer drawing its own
|
|
112
|
+
* arrowheads reads the tip here instead of extrapolating one. Each is present
|
|
113
|
+
* only when that end actually has an arrow (C `sflag`/`eflag`); with no arrow
|
|
114
|
+
* the field holds the calloc-zero point, which is not geometry.
|
|
115
|
+
*
|
|
116
|
+
* These are the *attachment* points on the node boundary, verbatim from the
|
|
117
|
+
* bezier — with `arrowhead=none` the spline simply ends there. Graphviz's own
|
|
118
|
+
* renderer insets the arrow polygon it draws by a penwidth-dependent amount
|
|
119
|
+
* (measured: ~1.5pt at `penwidth=1`, ~6.2pt at `penwidth=5`), so `ep` is the
|
|
120
|
+
* point to draw an arrow *to*, not a copy of the rendered polygon's tip.
|
|
121
|
+
*
|
|
109
122
|
* @see lib/common/types.h:ED_spl, ED_label, ED_tail_label, ED_head_label
|
|
123
|
+
* @see lib/common/types.h:bezier (sflag/eflag, sp/ep)
|
|
110
124
|
*/
|
|
111
125
|
export interface EdgeGeometry {
|
|
112
126
|
tail: string;
|
|
113
127
|
head: string;
|
|
114
128
|
/** Bezier control points for the edge spline, in points. */
|
|
115
129
|
points: { x: number; y: number }[];
|
|
130
|
+
/**
|
|
131
|
+
* Arrow attachment point at the tail end, if that end carries an arrow.
|
|
132
|
+
* @see lib/common/types.h:bezier.sp
|
|
133
|
+
*/
|
|
134
|
+
sp?: { x: number; y: number };
|
|
135
|
+
/**
|
|
136
|
+
* Arrow attachment point at the head end, if that end carries an arrow.
|
|
137
|
+
* @see lib/common/types.h:bezier.ep
|
|
138
|
+
*/
|
|
139
|
+
ep?: { x: number; y: number };
|
|
116
140
|
/** Centre edge label position, if present. @see lib/common/types.h:ED_label */
|
|
117
141
|
label?: { x: number; y: number };
|
|
118
142
|
/**
|
|
@@ -140,7 +164,15 @@ export interface EdgeGeometry {
|
|
|
140
164
|
* `class="cluster"` polygon, so a consumer quantizing to SVG precision gets
|
|
141
165
|
* byte-conformant geometry.
|
|
142
166
|
*
|
|
167
|
+
* `label` is the cluster title's placed position and measured size, present
|
|
168
|
+
* only when the cluster declares a label. Unlike the edge port labels, it is
|
|
169
|
+
* *not* gated on the label's `set` flag: C draws a cluster label on existence
|
|
170
|
+
* alone (emit.c:3920 has no `->set` test, unlike emit_edge_label:2891), so an
|
|
171
|
+
* unplaced one still renders at whatever pos it holds. Gating here would hide
|
|
172
|
+
* geometry `render()` acts on.
|
|
173
|
+
*
|
|
143
174
|
* @see lib/common/types.h:GD_bb (of a cluster subgraph)
|
|
175
|
+
* @see lib/common/types.h:GD_label (textlabel_t.pos / .dimen)
|
|
144
176
|
*/
|
|
145
177
|
export interface ClusterGeometry {
|
|
146
178
|
/** Cluster subgraph name (e.g. `cluster6`); encodes nesting. */
|
|
@@ -149,6 +181,13 @@ export interface ClusterGeometry {
|
|
|
149
181
|
y: number;
|
|
150
182
|
width: number;
|
|
151
183
|
height: number;
|
|
184
|
+
/**
|
|
185
|
+
* Cluster label placement, if the cluster has one. `x`/`y` are the **centre**
|
|
186
|
+
* of the label space (matching `EdgeGeometry.label`, not the box corner
|
|
187
|
+
* `x`/`y` above); `width`/`height` are its measured size.
|
|
188
|
+
* @see lib/common/postproc.c:place_graph_label
|
|
189
|
+
*/
|
|
190
|
+
label?: { x: number; y: number; width: number; height: number };
|
|
152
191
|
}
|
|
153
192
|
|
|
154
193
|
/**
|
|
@@ -237,10 +276,33 @@ function placedLabelPos(
|
|
|
237
276
|
return { x: lbl.pos.x, y: flipY(lbl.pos.y) };
|
|
238
277
|
}
|
|
239
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Arrow attachment points, one per end that actually carries an arrow.
|
|
281
|
+
* C keeps `sp`/`ep` beside the control points and gates them on
|
|
282
|
+
* `sflag`/`eflag`; with no arrow at that end the flag is 0 and the point is
|
|
283
|
+
* still calloc-zero, so an ungated read would publish (0, 0) as geometry.
|
|
284
|
+
* Indexes the bezier array by `spl.size` (not `list.length`), matching C.
|
|
285
|
+
* @see lib/common/types.h:bezier
|
|
286
|
+
* @see lib/common/postproc.c:endPoints
|
|
287
|
+
*/
|
|
288
|
+
function arrowAttachPoints(
|
|
289
|
+
edge: Edge,
|
|
290
|
+
flipY: (y: number) => number,
|
|
291
|
+
): { sp?: { x: number; y: number }; ep?: { x: number; y: number } } {
|
|
292
|
+
const spl = edge.info.spl;
|
|
293
|
+
if (spl === undefined || spl.size === 0) return {};
|
|
294
|
+
const out: { sp?: { x: number; y: number }; ep?: { x: number; y: number } } = {};
|
|
295
|
+
const first = spl.list[0];
|
|
296
|
+
if (first.sflag !== 0) out.sp = { x: first.sp.x, y: flipY(first.sp.y) };
|
|
297
|
+
const last = spl.list[spl.size - 1];
|
|
298
|
+
if (last.eflag !== 0) out.ep = { x: last.ep.x, y: flipY(last.ep.y) };
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
|
|
240
302
|
/**
|
|
241
303
|
* Snapshot one edge's geometry.
|
|
242
304
|
* @see lib/common/types.h:ED_spl, ED_label, ED_tail_label, ED_head_label
|
|
243
|
-
* (textlabel_t.pos)
|
|
305
|
+
* (textlabel_t.pos), bezier.sp/ep
|
|
244
306
|
*/
|
|
245
307
|
function snapshotEdge(edge: Edge, flipY: (y: number) => number): EdgeGeometry {
|
|
246
308
|
const geom: EdgeGeometry = {
|
|
@@ -256,6 +318,9 @@ function snapshotEdge(edge: Edge, flipY: (y: number) => number): EdgeGeometry {
|
|
|
256
318
|
if (tailLabel !== undefined) geom.tailLabel = tailLabel;
|
|
257
319
|
const headLabel = placedLabelPos(edge.info.head_label, flipY);
|
|
258
320
|
if (headLabel !== undefined) geom.headLabel = headLabel;
|
|
321
|
+
const { sp, ep } = arrowAttachPoints(edge, flipY);
|
|
322
|
+
if (sp !== undefined) geom.sp = sp;
|
|
323
|
+
if (ep !== undefined) geom.ep = ep;
|
|
259
324
|
return geom;
|
|
260
325
|
}
|
|
261
326
|
|
|
@@ -266,13 +331,18 @@ function snapshotEdge(edge: Edge, flipY: (y: number) => number): EdgeGeometry {
|
|
|
266
331
|
* native lower-left corner (ll); `yAxis:'down'` returns the top-left corner
|
|
267
332
|
* (ll.x, flipped ur.y). `width`/`height` are frame-independent (ur - ll).
|
|
268
333
|
*
|
|
334
|
+
* The label rides in the same call because place_graph_label runs on the same
|
|
335
|
+
* layout pass that fills GD_bb; `pos` is the centre of the label space, so it
|
|
336
|
+
* flips like any other coordinate rather than like the box corner.
|
|
337
|
+
*
|
|
269
338
|
* @see lib/common/types.h:GD_bb (cluster subgraph)
|
|
339
|
+
* @see lib/common/postproc.c:place_graph_label
|
|
270
340
|
*/
|
|
271
341
|
function snapshotCluster(
|
|
272
342
|
sg: Graph, yAxis: YAxis, flipY: (y: number) => number,
|
|
273
343
|
): ClusterGeometry {
|
|
274
344
|
const bb = sg.info.bb;
|
|
275
|
-
|
|
345
|
+
const geom: ClusterGeometry = {
|
|
276
346
|
name: sg.name,
|
|
277
347
|
x: bb.ll.x,
|
|
278
348
|
// 'up' keeps the lower-left y; 'down' flips the upper-right y to the
|
|
@@ -281,6 +351,16 @@ function snapshotCluster(
|
|
|
281
351
|
width: bb.ur.x - bb.ll.x,
|
|
282
352
|
height: bb.ur.y - bb.ll.y,
|
|
283
353
|
};
|
|
354
|
+
const lab = sg.info.label as TextlabelT | undefined;
|
|
355
|
+
if (lab !== undefined) {
|
|
356
|
+
geom.label = {
|
|
357
|
+
x: lab.pos.x,
|
|
358
|
+
y: flipY(lab.pos.y),
|
|
359
|
+
width: lab.dimen.x,
|
|
360
|
+
height: lab.dimen.y,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
return geom;
|
|
284
364
|
}
|
|
285
365
|
|
|
286
366
|
/**
|