@knowvah/dot-engine 2.7.0 → 2.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowvah/dot-engine",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
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",
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: EPL-2.0
2
+
3
+ /**
4
+ * Attribute reads with cgraph's `agxget` semantics: an object's own value,
5
+ * else the declared default it was created under. A leaf module (model types
6
+ * only) so label and shape code can share it without an import cycle.
7
+ *
8
+ * @see lib/cgraph/attr.c:agxget
9
+ */
10
+
11
+ import type { Node } from '../model/node.js';
12
+ import type { Graph } from '../model/graph.js';
13
+
14
+ /**
15
+ * Read a node attribute: the node's own value, else the node-attribute defaults
16
+ * (`node [...]`) captured from the node's declaring scope up to root *at the
17
+ * time the node was created*. The snapshot makes a `node [...]` declared after
18
+ * the node — at any scope — correctly not apply, and lets a cluster-scoped
19
+ * default reach nodes declared in that cluster, as the oracle does.
20
+ *
21
+ * Programmatically created nodes have no snapshot; they fall back to the live
22
+ * defaults of the passed graph `g` and root.
23
+ * @see lib/cgraph/attr.c:agxget; src/parser/builder.ts:effectiveNodeDefaults
24
+ */
25
+ export function nodeAttr(n: Node, g: Graph, key: string): string | undefined {
26
+ const own = n.attrs.get(key);
27
+ if (own !== undefined) return own;
28
+ if (n.nodeDefaultsSnapshot !== undefined) return n.nodeDefaultsSnapshot.get(key);
29
+ return g.nodeDefaults.get(key)
30
+ ?? (g !== g.root ? g.root.nodeDefaults.get(key) : undefined);
31
+ }
32
+
33
+ /**
34
+ * Read a graph attribute with subgraph→root inheritance, mirroring C's
35
+ * agxget (late_nnstring(sg, agfindgraphattr(sg, key), ...) in do_graph_label):
36
+ * a cluster inherits `fontname`/`fontsize`/`fontcolor` set on an ancestor
37
+ * (e.g. root `graph[fontname=Arial]`) when it sets none of its own.
38
+ * @see lib/common/input.c:do_graph_label
39
+ */
40
+ export function graphAttrInherited(sg: Graph, key: string): string | undefined {
41
+ // Own value wins; otherwise the ancestor defaults SNAPSHOTTED when this
42
+ // subgraph opened (not a live parent walk) — so an ancestor attribute set
43
+ // later in statement order does not retroactively apply (2184).
44
+ // @see lib/cgraph/graph.c:agsubg (parse-time defval copy)
45
+ return sg.attrs.get(key) ?? sg.graphDefaultsSnapshot?.get(key);
46
+ }
@@ -16,6 +16,8 @@ import { makeHtmlLabelTo } from './htmltable-label.js';
16
16
  import { labelStderr } from './htmltable-attr-warn.js';
17
17
  import { substObj, type GraphObj } from './subst.js';
18
18
  import { Edge } from '../model/edge.js';
19
+ import { Node } from '../model/node.js';
20
+ import { nodeAttr, graphAttrInherited } from './attr-get.js';
19
21
  import { Graph } from '../model/graph.js';
20
22
  import { htmlEntityUTF8 } from './html-entities.js';
21
23
 
@@ -33,17 +35,27 @@ export { htmlEntityUTF8 };
33
35
  /**
34
36
  * The object's pen color: `pencolor` attr, else `color` attr, else undefined
35
37
  * (no default). An HTML table/cell border with no explicit COLOR inherits it.
38
+ * Read as C's agget does, so declared defaults count: a node's `node [...]`
39
+ * snapshot and a subgraph's inherited graph attributes (894589). Edge
40
+ * defaults are already materialized into `attrs` by the builder.
36
41
  * @see lib/common/htmltable.c:getPenColor
37
42
  */
38
43
  function getPenColor(obj?: GraphObj): string | undefined {
39
44
  if (obj === undefined) return undefined;
40
- const pc = obj.attrs.get('pencolor');
45
+ const pc = aggetObj(obj, 'pencolor');
41
46
  if (pc !== undefined && pc !== '') return pc;
42
- const c = obj.attrs.get('color');
47
+ const c = aggetObj(obj, 'color');
43
48
  if (c !== undefined && c !== '') return c;
44
49
  return undefined;
45
50
  }
46
51
 
52
+ /** `agget(obj, key)` for any object kind. @see lib/cgraph/attr.c:agxget */
53
+ function aggetObj(obj: GraphObj, key: string): string | undefined {
54
+ if (obj instanceof Node) return nodeAttr(obj, obj.root, key);
55
+ if (obj instanceof Graph) return graphAttrInherited(obj, key);
56
+ return obj.attrs.get(key);
57
+ }
58
+
47
59
  /** The root graph owning `obj` (edges resolve through their tail node). */
48
60
  function rootOf(obj: GraphObj): Graph {
49
61
  return obj instanceof Edge ? obj.tail.root : obj.root;
@@ -10,6 +10,7 @@
10
10
  import type { Node } from '../model/node.js';
11
11
  import type { Graph } from '../model/graph.js';
12
12
  import type { TextMeasurer } from './textmeasure.js';
13
+ import { nodeAttr } from './attr-get.js';
13
14
  import type { PolygonT, ShapeDesc } from './types.js';
14
15
  import { ShapeKind } from './types.js';
15
16
  import type { TextlabelT } from './types.js';
@@ -32,24 +33,9 @@ const DEFAULT_LW = 27;
32
33
  const DEFAULT_RW = 27;
33
34
  const DEFAULT_HT = 36;
34
35
 
35
- /**
36
- * Read a node attribute: the node's own value, else the node-attribute defaults
37
- * (`node [...]`) captured from the node's declaring scope up to root *at the
38
- * time the node was created*. The snapshot makes a `node [...]` declared after
39
- * the node — at any scope — correctly not apply, and lets a cluster-scoped
40
- * default reach nodes declared in that cluster, as the oracle does.
41
- *
42
- * Programmatically created nodes have no snapshot; they fall back to the live
43
- * defaults of the passed graph `g` and root.
44
- * @see lib/cgraph/attr.c:agxget; src/parser/builder.ts:effectiveNodeDefaults
45
- */
46
- export function nodeAttr(n: Node, g: Graph, key: string): string | undefined {
47
- const own = n.attrs.get(key);
48
- if (own !== undefined) return own;
49
- if (n.nodeDefaultsSnapshot !== undefined) return n.nodeDefaultsSnapshot.get(key);
50
- return g.nodeDefaults.get(key)
51
- ?? (g !== g.root ? g.root.nodeDefaults.get(key) : undefined);
52
- }
36
+ // nodeAttr moved to attr-get.ts (leaf module, shared with make-label.ts);
37
+ // re-exported so existing importers are unchanged.
38
+ export { nodeAttr };
53
39
 
54
40
  /** Read label-font attributes from node, falling back to defaults. */
55
41
  export function readFontAttrs(n: Node, g: Graph): {
@@ -9,6 +9,7 @@
9
9
  import type { Graph } from '../../model/graph.js';
10
10
  import type { Point } from '../../model/geom.js';
11
11
  import type { TextMeasurer } from '../../common/textmeasure.js';
12
+ import { graphAttrInherited } from '../../common/attr-get.js';
12
13
  import { makeAnyLabel, DEFAULT_FONTSIZE, DEFAULT_FONTNAME, DEFAULT_COLOR } from '../../common/make-label.js';
13
14
  import { isHtmlValue, htmlValueContent } from '../../common/html-string.js';
14
15
  import { BOTTOM_IX, TOP_IX, RIGHT_IX, LEFT_IX } from './position-aux.js';
@@ -45,21 +46,6 @@ function readLabelPos(sg: Graph): number {
45
46
  return pos;
46
47
  }
47
48
 
48
- /**
49
- * Read a graph attribute with subgraph→root inheritance, mirroring C's
50
- * agxget (late_nnstring(sg, agfindgraphattr(sg, key), ...) in do_graph_label):
51
- * a cluster inherits `fontname`/`fontsize`/`fontcolor` set on an ancestor
52
- * (e.g. root `graph[fontname=Arial]`) when it sets none of its own.
53
- * @see lib/common/input.c:do_graph_label
54
- */
55
- function graphAttrInherited(sg: Graph, key: string): string | undefined {
56
- // Own value wins; otherwise the ancestor defaults SNAPSHOTTED when this
57
- // subgraph opened (not a live parent walk) — so an ancestor attribute set
58
- // later in statement order does not retroactively apply (2184).
59
- // @see lib/cgraph/graph.c:agsubg (parse-time defval copy)
60
- return sg.attrs.get(key) ?? sg.graphDefaultsSnapshot?.get(key);
61
- }
62
-
63
49
  /**
64
50
  * Graph/cluster label font, read through graphAttrInherited.
65
51
  * @internal also used by src/async/collect.ts to request the same face.