@davidsouther/agathon 0.0.0-stage → 2026.11.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.
Files changed (62) hide show
  1. package/package.json +19 -4
  2. package/src/assert.ts +67 -0
  3. package/src/geometry/box.ts +76 -0
  4. package/src/geometry/circle.ts +296 -0
  5. package/src/geometry/geometry.ts +318 -0
  6. package/src/geometry/path.ts +315 -0
  7. package/src/geometry/quadtree.ts +321 -0
  8. package/src/geometry/rectangle.ts +129 -0
  9. package/src/geometry/rngon.ts +132 -0
  10. package/src/geometry/types.ts +102 -0
  11. package/src/geometry.ts +61 -0
  12. package/src/highlight/highlight.ts +85 -0
  13. package/src/index.ts +5 -0
  14. package/src/layout/grid/manager.ts +55 -0
  15. package/src/layout/grid.ts +1 -0
  16. package/src/layout/hierarchic/README.md +21 -0
  17. package/src/layout/hierarchic/acyclic.ts +249 -0
  18. package/src/layout/hierarchic/children.ts +169 -0
  19. package/src/layout/hierarchic/debug.ts +186 -0
  20. package/src/layout/hierarchic/layout.ts +266 -0
  21. package/src/layout/hierarchic/manager.ts +65 -0
  22. package/src/layout/hierarchic/normalize.ts +177 -0
  23. package/src/layout/hierarchic/order.ts +321 -0
  24. package/src/layout/hierarchic/position.ts +514 -0
  25. package/src/layout/hierarchic/rank.ts +629 -0
  26. package/src/layout/hierarchic.ts +2 -0
  27. package/src/layout/hulls/arrange.ts +94 -0
  28. package/src/layout/hulls/hulls.ts +243 -0
  29. package/src/layout/hulls/segments.ts +174 -0
  30. package/src/layout/hulls.ts +3 -0
  31. package/src/layout/manager/child_manager.ts +42 -0
  32. package/src/layout/manager/drag_manager.ts +112 -0
  33. package/src/layout/manager/manager.ts +425 -0
  34. package/src/layout/manager/pan_manager.ts +146 -0
  35. package/src/layout/manager/visibility_manager.ts +84 -0
  36. package/src/layout/manager/zoom_manager.ts +263 -0
  37. package/src/layout/manager.ts +6 -0
  38. package/src/layout/properties/defaults.ts +99 -0
  39. package/src/layout/properties/symbol.ts +99 -0
  40. package/src/layout/properties/weak_map.ts +73 -0
  41. package/src/layout/properties.ts +111 -0
  42. package/src/layout/types.ts +470 -0
  43. package/src/layout/util.ts +444 -0
  44. package/src/layout.ts +6 -0
  45. package/src/samples/diff_graph.ts +15 -0
  46. package/src/samples/edge_cases.ts +168 -0
  47. package/src/samples/gansner_with_states.ts +67 -0
  48. package/src/samples/load_generator.ts +213 -0
  49. package/src/samples/search_graph.ts +35 -0
  50. package/src/tsabre/algo.ts +126 -0
  51. package/src/tsabre/builder.ts +97 -0
  52. package/src/tsabre/compress.ts +15 -0
  53. package/src/tsabre/dijkstra/dijkstra.ts +76 -0
  54. package/src/tsabre/dijkstra/heap.ts +83 -0
  55. package/src/tsabre/graph.ts +808 -0
  56. package/src/tsabre/parse.ts +634 -0
  57. package/src/tsabre/print.ts +188 -0
  58. package/src/tsabre/sourcebuilder/code_content_buffer.ts +275 -0
  59. package/src/tsabre/sourcebuilder/printable.ts +33 -0
  60. package/src/tsabre.ts +6 -0
  61. package/src/types.ts +424 -0
  62. package/README.md +0 -3
@@ -0,0 +1,263 @@
1
+ import type { Geometry } from "../../geometry/types.ts";
2
+ import { CircleGeometry } from "../../geometry.ts";
3
+ import type { Point, Segment } from "../../types.ts";
4
+ import type { LayoutEdge, LayoutNode, SegmentPosition } from "../types.ts";
5
+ import {
6
+ getGraphProps,
7
+ getLineSegmentBetweenNodes,
8
+ getNodeProps,
9
+ getSegmentProps,
10
+ } from "../util.ts";
11
+
12
+ import type { NoopLayoutManager } from "./manager.ts";
13
+
14
+ const ZOOM_LEVELS_PER_WHEEL = 1;
15
+ const ZOOM_EXPANSION_RATIO = 10;
16
+
17
+ /**
18
+ * Noop zoom manager which handles setting positions but does not update zoom
19
+ * level.
20
+ */
21
+ export class NoopZoomManager {
22
+ protected zoomLevel = 0;
23
+ protected expansionFactor = 1;
24
+
25
+ get level(): number {
26
+ return this.zoomLevel;
27
+ }
28
+
29
+ protected readonly layoutManager: NoopLayoutManager;
30
+ constructor(layoutManager: NoopLayoutManager) {
31
+ this.layoutManager = layoutManager;
32
+ }
33
+
34
+ dragAmount({ x, y }: Point): Point {
35
+ return {
36
+ x: x / this.expansionFactor,
37
+ y: y / this.expansionFactor,
38
+ };
39
+ }
40
+
41
+ zoomIn(center?: Point): void {
42
+ this.zoom(this.zoomLevel + ZOOM_LEVELS_PER_WHEEL, center);
43
+ }
44
+
45
+ zoomOut(center?: Point): void {
46
+ this.zoom(this.zoomLevel - ZOOM_LEVELS_PER_WHEEL, center);
47
+ }
48
+
49
+ zoom(level: number, center: Point | undefined): void {
50
+ // noop
51
+ }
52
+
53
+ /**
54
+ * Given a point, project it along its axis away from the center of the
55
+ * layout by `expansionFactor` amount. That is, if the point was 5 units from
56
+ * the center, and the expansionFactor is 2 (zoom level 10), the new position
57
+ * would be 10 units away along the same line.
58
+ */
59
+ diffusePoint({ x, y }: Point, { x: cx, y: cy } = this.layoutManager.center): Point {
60
+ return {
61
+ x: place(x, this.expansionFactor, cx),
62
+ y: place(y, this.expansionFactor, cy),
63
+ };
64
+ }
65
+
66
+ undiffusePoint({ x, y }: Point, { x: cx, y: cy } = this.layoutManager.center): Point {
67
+ return {
68
+ x: place(x, 1 / this.expansionFactor, cx),
69
+ y: place(y, 1 / this.expansionFactor, cy),
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Given a node and its position, calculate a new node position with all
75
+ * points diffused by expansionFactor away. Top level nodes are moved
76
+ * relative to the center of the layout; lower nodes are moved relative to the
77
+ * <0, 0> center of the hull. Hull sizes are recalculated as the smallest
78
+ * bounding circle.
79
+ */
80
+ diffuseNodePosition(node: LayoutNode, position: Geometry): Geometry {
81
+ const { baseRadius } = getGraphProps(this.layoutManager.graph);
82
+ const isTopLevel = !this.layoutManager.graph.hasNode(this.layoutManager.graph.parent(node.id));
83
+ position = position.clone();
84
+ position.moveTo(this.diffusePoint(position, isTopLevel ? undefined : { x: 0, y: 0 }));
85
+
86
+ if (node.expanded && this.layoutManager.graph.hasChildren(node.id)) {
87
+ // only recalculate node size if this expanded node has children. If it
88
+ // doesn't have children, the size is set by a constraint other than
89
+ // "smallest enclosing circle"
90
+ // Recursively layout all children first
91
+ const children: Geometry[] = [...this.layoutManager.graph.children(node.id)].map((cid) => {
92
+ try {
93
+ return this.layoutManager.getNodePosition(cid);
94
+ } catch (e: unknown) {
95
+ // TODO: Handle this exception better.
96
+ return getNodeProps(this.layoutManager.getNode(cid)).geometry;
97
+ }
98
+ });
99
+
100
+ // Find the smallest bounding circle
101
+ position.fitTo(children, baseRadius);
102
+ }
103
+
104
+ return position;
105
+ }
106
+
107
+ /**
108
+ * Given a segment, diffuse its position, but keep endpoints connected to
109
+ * their node positions. Node positions must be calculated before calculating
110
+ * segment positions.
111
+ */
112
+ diffuseSegmentPosition(segment: Segment<LayoutEdge>, position: SegmentPosition): SegmentPosition {
113
+ if (!segment.next) {
114
+ position = this.diffuseSiblingSegment(segment, position);
115
+ } else {
116
+ position = this.diffuseRisingSegment(segment, position);
117
+ }
118
+
119
+ position = this.diffuseSegmentLabel(segment, position);
120
+
121
+ return position;
122
+ }
123
+
124
+ protected diffuseSiblingSegment(
125
+ segment: Segment<LayoutEdge>,
126
+ position: SegmentPosition,
127
+ ): SegmentPosition {
128
+ const length = position.points.length;
129
+
130
+ const { points } = getSegmentProps(segment);
131
+ for (let i = 0; i < length; i++) {
132
+ position.points[i] = this.diffusePoint(points[i]);
133
+ }
134
+
135
+ return this.clipSiblingSegment(segment, position);
136
+ }
137
+
138
+ protected clipSiblingSegment(
139
+ segment: Segment<LayoutEdge>,
140
+ position: SegmentPosition,
141
+ ): SegmentPosition {
142
+ const length = position.points.length;
143
+ if (length < 2) {
144
+ // It's unclear how a segment will have less than two points. However that
145
+ // happened, there's no way to "correct" the end points relative to one
146
+ // another, so we skip fixing the first and last point to the node
147
+ // centers.
148
+ return position;
149
+ }
150
+
151
+ const source = this.layoutManager.getNodePosition(segment.source);
152
+ const target = this.layoutManager.getNodePosition(segment.target);
153
+
154
+ const hasIntermediatePoints = length > 2;
155
+ const nextPoint = hasIntermediatePoints ? CircleGeometry.point(position.points[1]) : target;
156
+ const lastPoint = hasIntermediatePoints
157
+ ? CircleGeometry.point(position.points[length - 2])
158
+ : source;
159
+
160
+ position.points[0] = getLineSegmentBetweenNodes(source, nextPoint).source;
161
+ position.points[length - 1] = getLineSegmentBetweenNodes(lastPoint, target).target;
162
+
163
+ return position;
164
+ }
165
+
166
+ protected diffuseRisingSegment(
167
+ segment: Segment<LayoutEdge>,
168
+ position: SegmentPosition,
169
+ ): SegmentPosition {
170
+ const next = segment.next;
171
+ if (!next) return this.diffuseSiblingSegment(segment, position);
172
+ const nextPosition = this.layoutManager.getSegmentPosition(next);
173
+
174
+ // source point is source node position, source node radius
175
+ const source = this.layoutManager.getNodePosition(segment.source);
176
+
177
+ // target point is segment.next.points[0] (or [l] if segment is
178
+ // top-level-reversed), translated to local node space
179
+ const nextPoint = next.source === segment.target ? 0 : nextPosition.points.length - 1;
180
+ const targetPoint = { ...nextPosition.points[nextPoint] };
181
+ const target = this.layoutManager.getNodePosition(segment.target);
182
+ targetPoint.x -= target.x;
183
+ targetPoint.y -= target.y;
184
+
185
+ const line = getLineSegmentBetweenNodes(source, CircleGeometry.point(targetPoint));
186
+ position.points[0] = line.source;
187
+ position.points[1] = line.target;
188
+ position.points.length = 2;
189
+
190
+ return position;
191
+ }
192
+
193
+ private diffuseSegmentLabel(
194
+ segment: Segment<LayoutEdge>,
195
+ position: SegmentPosition,
196
+ ): SegmentPosition {
197
+ if (position.points.length < 2) return position;
198
+
199
+ const median = (position.points.length / 2) | 0;
200
+ let { x: lsx, y: lsy } = position.points[median - 1];
201
+ let { x: ltx, y: lty } = position.points[median];
202
+ if (lsx > ltx) {
203
+ const [ttx, tty] = [ltx, lty];
204
+ [ltx, lty] = [lsx, lsy];
205
+ [lsx, lsy] = [ttx, tty];
206
+ position.label.flippedBy180 = true;
207
+ } else {
208
+ position.label.flippedBy180 = false;
209
+ }
210
+
211
+ position.label.position.x = Math.round(lsx / 2 + ltx / 2);
212
+ position.label.position.y = Math.round(lsy / 2 + lty / 2);
213
+ position.label.rotation = (Math.atan2(lty - lsy, ltx - lsx) * 180) / Math.PI;
214
+
215
+ return position;
216
+ }
217
+ }
218
+
219
+ /** Zoom manager which actually updates zoom based on requested levels. */
220
+ export class ZoomManager extends NoopZoomManager {
221
+ constructor(layoutManager: NoopLayoutManager) {
222
+ super(layoutManager);
223
+ }
224
+
225
+ override zoom(
226
+ level: number,
227
+ center: Point = {
228
+ x: this.layoutManager.pageBounds.width / 2,
229
+ y: this.layoutManager.pageBounds.height / 2,
230
+ },
231
+ ): void {
232
+ // Given the center point relative to the layoutManager's page bounds,
233
+ // determine its equivalent position in the layout. The equivalent position
234
+ // is determined by having pan manager remove the relative offset and zoom
235
+ // values.
236
+ const projectedPointOnGraph = this.layoutManager.pan.pagePointToGraphPoint(center);
237
+
238
+ // Adjust level and expansion factor
239
+ this.zoomLevel = Math.min(level, this.layoutManager.depth * ZOOM_EXPANSION_RATIO);
240
+ this.expansionFactor = 2 ** (this.zoomLevel / ZOOM_EXPANSION_RATIO);
241
+
242
+ // After changing the expansion factor, convert the projected point back to
243
+ // page space, and calculate the delta necessary to adjust the graph to keep
244
+ // the center point at the same relative bounds position.
245
+ const correctedPointInScreenSpace =
246
+ this.layoutManager.pan.graphPointToPagePoint(projectedPointOnGraph);
247
+ const delta = {
248
+ x: center.x - correctedPointInScreenSpace.x,
249
+ y: center.y - correctedPointInScreenSpace.y,
250
+ };
251
+
252
+ // Recalculate positions and update the graph.
253
+ this.layoutManager.moveGraphBy(delta);
254
+ this.layoutManager.updateAt();
255
+ }
256
+ }
257
+
258
+ // Given two points on the real number line, return the point p such that the
259
+ // distance from p to c is equal to the distance from n to c times a factor.
260
+ // This call is highly homomorphic, and will get inlined
261
+ function place(n: number, factor: number, c: number): number {
262
+ return c + factor * (n - c);
263
+ }
@@ -0,0 +1,6 @@
1
+ export { ChildManager } from "./manager/child_manager.ts";
2
+ export { DragManager } from "./manager/drag_manager.ts";
3
+ export { NoopLayoutManager } from "./manager/manager.ts";
4
+ export { PanManager } from "./manager/pan_manager.ts";
5
+ export { VisibilityManager } from "./manager/visibility_manager.ts";
6
+ export { ZoomManager } from "./manager/zoom_manager.ts";
@@ -0,0 +1,99 @@
1
+ import { CircleGeometry } from "../../geometry.ts";
2
+ import type { NodeId } from "../../types.ts";
3
+
4
+ import type {
5
+ EdgeConfig,
6
+ EdgeProperties,
7
+ GraphConfig,
8
+ GraphProperties,
9
+ NodeConfig,
10
+ NodeProperties,
11
+ SegmentConfig,
12
+ SegmentProperties,
13
+ } from "../types.ts";
14
+
15
+ /**
16
+ * Default properties for a node.
17
+ *
18
+ * Makes a fresh object every call.
19
+ */
20
+ export function defaultNodeProperties(layout: Partial<NodeConfig> = {}): NodeProperties {
21
+ return {
22
+ geometry: new CircleGeometry({ radius: 20 }),
23
+ rank: 0,
24
+ // Start order at -1 so initOrder knows whether to push or assign
25
+ // in the row.
26
+ order: -1,
27
+ lim: Number.MAX_SAFE_INTEGER,
28
+ low: 1,
29
+ virtual: false,
30
+ fixedRank: false,
31
+ ...layout,
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Default properties for an edge.
37
+ *
38
+ * Makes a fresh object every call.
39
+ */
40
+ export function defaultEdgeProperties(layout: Partial<EdgeConfig> = {}): EdgeProperties {
41
+ return {
42
+ minLength: 1,
43
+ weight: 1,
44
+ cutValue: 0,
45
+ reversed: false,
46
+ nestingEdge: false,
47
+ points: [],
48
+ ...layout,
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Default properties for a segment.
54
+ *
55
+ * Makes a fresh object every call.
56
+ */
57
+ export function defaultSegmentProperties(layout: Partial<SegmentConfig> = {}): SegmentProperties {
58
+ return {
59
+ metrics: {
60
+ forward: undefined,
61
+ reverse: undefined,
62
+ },
63
+ annotations: {
64
+ forward: undefined,
65
+ reverse: undefined,
66
+ },
67
+ points: [],
68
+ label: {
69
+ position: { x: 0, y: 0 },
70
+ rotation: 0,
71
+ flippedBy180: false,
72
+ },
73
+ ...layout,
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Default properties for a graph.
79
+ *
80
+ * Makes a fresh object every call.
81
+ */
82
+ export function defaultGraphProperties(layout: Partial<GraphConfig> = {}): GraphProperties {
83
+ return {
84
+ rankSep: 50,
85
+ edgeSep: 10,
86
+ nodeSep: 50,
87
+ nodeRankFactor: 1,
88
+ baseRadius: 20,
89
+ hullRotation: 25,
90
+ childrenRelative: false,
91
+ bendLongEdges: true,
92
+ staggerRows: 10,
93
+ timing: false,
94
+ printDebug: false,
95
+ virtualNodeLayout: new Map<NodeId, { rank: number; order: number }>(),
96
+ fas: undefined,
97
+ ...layout,
98
+ };
99
+ }
@@ -0,0 +1,99 @@
1
+ import type { Edge, Node, Segment, SmartGraph } from "../../types.ts";
2
+ import type {
3
+ EdgeConfig,
4
+ EdgeProperties,
5
+ GraphProperties,
6
+ NodeConfig,
7
+ NodeProperties,
8
+ PropertiesStrategy,
9
+ SegmentConfig,
10
+ SegmentProperties,
11
+ } from "../types.ts";
12
+
13
+ import {
14
+ defaultEdgeProperties,
15
+ defaultGraphProperties,
16
+ defaultNodeProperties,
17
+ defaultSegmentProperties,
18
+ } from "./defaults.ts";
19
+
20
+ const LAYOUT: unique symbol = Symbol("Layout");
21
+
22
+ /**
23
+ * A PropertiesStrategy which stores properties in a private Symbol on entities.
24
+ */
25
+ export const LAYOUT_SYMBOL_PROPERTIES_STRATEGY: PropertiesStrategy = {
26
+ getNodeProps(
27
+ obj: Node & {
28
+ layout?: Partial<NodeConfig>;
29
+ [LAYOUT]?: NodeProperties;
30
+ },
31
+ ): NodeProperties {
32
+ return (obj[LAYOUT] = obj[LAYOUT] ?? {
33
+ ...defaultNodeProperties(obj.layout),
34
+ });
35
+ },
36
+
37
+ getEdgeProps(
38
+ obj: Edge & {
39
+ layout?: Partial<EdgeConfig>;
40
+ [LAYOUT]?: EdgeProperties;
41
+ },
42
+ ): EdgeProperties {
43
+ return (obj[LAYOUT] = obj[LAYOUT] ?? defaultEdgeProperties(obj.layout));
44
+ },
45
+
46
+ getSegmentProps(
47
+ obj: Segment & {
48
+ layout?: Partial<SegmentConfig>;
49
+ [LAYOUT]?: SegmentProperties;
50
+ },
51
+ ): SegmentProperties {
52
+ return (obj[LAYOUT] = obj[LAYOUT] ?? defaultSegmentProperties(obj.layout));
53
+ },
54
+
55
+ getGraphProps(
56
+ obj: SmartGraph<{}, {}> & {
57
+ layout?: Partial<GraphProperties>;
58
+ [LAYOUT]?: GraphProperties;
59
+ },
60
+ ): GraphProperties {
61
+ return (obj[LAYOUT] = obj[LAYOUT] ?? defaultGraphProperties(obj.layout));
62
+ },
63
+
64
+ clearNodeProps(
65
+ obj: Node & {
66
+ layout?: Partial<NodeProperties>;
67
+ [LAYOUT]?: NodeProperties;
68
+ },
69
+ ) {
70
+ obj[LAYOUT] = undefined;
71
+ },
72
+
73
+ clearEdgeProps(
74
+ obj: Edge & {
75
+ layout?: Partial<EdgeProperties>;
76
+ [LAYOUT]?: EdgeProperties;
77
+ },
78
+ ) {
79
+ obj[LAYOUT] = undefined;
80
+ },
81
+
82
+ clearSegmentProps(
83
+ obj: Segment & {
84
+ layout?: Partial<SegmentConfig>;
85
+ [LAYOUT]?: EdgeProperties;
86
+ },
87
+ ) {
88
+ obj[LAYOUT] = undefined;
89
+ },
90
+
91
+ clearGraphProps(
92
+ obj: SmartGraph<{}, {}> & {
93
+ layout?: Partial<GraphProperties>;
94
+ [LAYOUT]?: GraphProperties;
95
+ },
96
+ ) {
97
+ obj[LAYOUT] = undefined;
98
+ },
99
+ };
@@ -0,0 +1,73 @@
1
+ import type { Edge, Node, Segment, SmartGraph } from "../../types.ts";
2
+ import type {
3
+ EdgeConfig,
4
+ EdgeProperties,
5
+ GraphProperties,
6
+ NodeConfig,
7
+ NodeProperties,
8
+ PropertiesStrategy,
9
+ SegmentConfig,
10
+ SegmentProperties,
11
+ } from "../types.ts";
12
+
13
+ import {
14
+ defaultEdgeProperties,
15
+ defaultGraphProperties,
16
+ defaultNodeProperties,
17
+ defaultSegmentProperties,
18
+ } from "./defaults.ts";
19
+
20
+ const EDGE_MAP = new WeakMap<Edge, EdgeProperties>();
21
+ const GRAPH_MAP = new WeakMap<SmartGraph<{}, {}>, GraphProperties>();
22
+ const NODE_MAP = new WeakMap<Node, NodeProperties>();
23
+ const SEGMENT_MAP = new WeakMap<Segment, SegmentProperties>();
24
+
25
+ /**
26
+ * A PropertiesStrategy which stores properties in a WeakMap from entity to
27
+ * properties.
28
+ */
29
+ export const WEAK_MAP_PROPERTIES_STRATEGY: PropertiesStrategy = {
30
+ getNodeProps(obj: Node & { layout?: Partial<NodeConfig> }): NodeProperties {
31
+ const properties = NODE_MAP.get(obj) ?? {
32
+ ...defaultNodeProperties(obj.layout),
33
+ };
34
+ NODE_MAP.set(obj, properties);
35
+ return properties;
36
+ },
37
+
38
+ getEdgeProps(obj: Edge & { layout?: Partial<EdgeConfig> }): EdgeProperties {
39
+ const properties = EDGE_MAP.get(obj) ?? {
40
+ ...defaultEdgeProperties(obj.layout),
41
+ };
42
+ EDGE_MAP.set(obj, properties);
43
+ return properties;
44
+ },
45
+
46
+ getSegmentProps(obj: Segment & { layout?: Partial<SegmentConfig> }): SegmentProperties {
47
+ const properties = SEGMENT_MAP.get(obj) ?? defaultSegmentProperties(obj.layout);
48
+ SEGMENT_MAP.set(obj, properties);
49
+ return properties;
50
+ },
51
+
52
+ getGraphProps(obj: SmartGraph<{}, {}> & { layout?: Partial<GraphProperties> }): GraphProperties {
53
+ const properties = GRAPH_MAP.get(obj) ?? defaultGraphProperties(obj.layout);
54
+ GRAPH_MAP.set(obj, properties);
55
+ return properties;
56
+ },
57
+
58
+ clearNodeProps(obj: Node) {
59
+ NODE_MAP.delete(obj);
60
+ },
61
+
62
+ clearEdgeProps(obj: Edge) {
63
+ EDGE_MAP.delete(obj);
64
+ },
65
+
66
+ clearSegmentProps(obj: Segment) {
67
+ SEGMENT_MAP.delete(obj);
68
+ },
69
+
70
+ clearGraphProps(obj: SmartGraph<{}, {}>) {
71
+ GRAPH_MAP.delete(obj);
72
+ },
73
+ };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @fileoverview utilities to track properties for Agathon entities.
3
+ *
4
+ * The Agathon layout engine tracks numerous properties about a graph and its
5
+ * nodes, edges, and segments. Historically, these were bundled per instance in
6
+ * a property grab bag hidden behind a symbol. While quick to implement at the
7
+ * time, and it certainly gets the job done, it is unclear whether how this
8
+ * approach handles a variety of situations from performance or other stand
9
+ * points.
10
+ *
11
+ * This centralized property lookup tool lets clients of Agathon choose which
12
+ * mechanism works best for their workload.
13
+ */
14
+ export { LAYOUT_SYMBOL_PROPERTIES_STRATEGY } from "./properties/symbol.ts";
15
+ export { WEAK_MAP_PROPERTIES_STRATEGY } from "./properties/weak_map.ts";
16
+
17
+ import type { Edge, Node, Segment, SmartGraph } from "../types.ts";
18
+ import { LAYOUT_SYMBOL_PROPERTIES_STRATEGY } from "./properties/symbol.ts";
19
+ import type {
20
+ EdgeConfig,
21
+ EdgeProperties,
22
+ GraphProperties,
23
+ NodeConfig,
24
+ NodeProperties,
25
+ PropertiesStrategy,
26
+ SegmentConfig,
27
+ SegmentProperties,
28
+ } from "./types.ts";
29
+
30
+ let propertyStrategy: PropertiesStrategy;
31
+
32
+ /**
33
+ * Choose a global PropertiesStrategy to be used for all Agathon layout
34
+ * calculations.
35
+ */
36
+ export function setPropertiesStrategy(strategy: PropertiesStrategy): void {
37
+ propertyStrategy = strategy;
38
+ }
39
+
40
+ /**
41
+ * Reset the PropertiesStrategy to be used for Agathon layout calculations.
42
+ */
43
+ export function resetPropertiesStrategy(): void {
44
+ setPropertiesStrategy(LAYOUT_SYMBOL_PROPERTIES_STRATEGY);
45
+ }
46
+
47
+ resetPropertiesStrategy();
48
+
49
+ /**
50
+ * Initialize layout properties, pulling values from the layout properties if
51
+ * provided for initial values.
52
+ */
53
+ export function getNodeProps(obj: Node & { layout?: Partial<NodeConfig> }): NodeProperties {
54
+ return propertyStrategy.getNodeProps(obj);
55
+ }
56
+
57
+ /**
58
+ * Initialize layout properties for an edge, pulling values from the layout
59
+ * properties if provided for initial values.
60
+ */
61
+ export function getEdgeProps(obj: Edge & { layout?: Partial<EdgeConfig> }): EdgeProperties {
62
+ return propertyStrategy.getEdgeProps(obj);
63
+ }
64
+
65
+ /**
66
+ * Initialize layout properties for a segment, pulling values from the layout
67
+ * properties if provided for initial values.
68
+ */
69
+ export function getSegmentProps(
70
+ obj: Segment & { layout?: Partial<SegmentConfig> },
71
+ ): SegmentProperties {
72
+ return propertyStrategy.getSegmentProps(obj);
73
+ }
74
+
75
+ /**
76
+ * Initialize layout properties for a graph, pulling values from the layout
77
+ * properties if provided for initial values.
78
+ */
79
+ export function getGraphProps(
80
+ obj: SmartGraph<{}, {}> & { layout?: Partial<GraphProperties> },
81
+ ): GraphProperties {
82
+ return propertyStrategy.getGraphProps(obj);
83
+ }
84
+
85
+ /**
86
+ * Clear layout properties for a node.
87
+ */
88
+ export function clearNodeProps(obj: Node): void {
89
+ propertyStrategy.clearNodeProps(obj);
90
+ }
91
+
92
+ /**
93
+ * Clear layout properties for an edge.
94
+ */
95
+ export function clearEdgeProps(obj: Edge): void {
96
+ propertyStrategy.clearEdgeProps(obj);
97
+ }
98
+
99
+ /**
100
+ * Clear layout properties for a segment.
101
+ */
102
+ export function clearSegmentProps(obj: Segment): void {
103
+ propertyStrategy.clearSegmentProps(obj);
104
+ }
105
+
106
+ /**
107
+ * Clear layout properties for a graph.
108
+ */
109
+ export function clearGraphProps(obj: SmartGraph<{}, {}>): void {
110
+ propertyStrategy.clearGraphProps(obj);
111
+ }