@ngx-km/layout 0.0.1 → 0.0.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/fesm2022/ngx-km-layout.mjs +101 -31
- package/fesm2022/ngx-km-layout.mjs.map +1 -1
- package/index.d.ts +119 -8
- package/package.json +1 -1
|
@@ -8,16 +8,53 @@ import { InjectionToken, inject, Injectable } from '@angular/core';
|
|
|
8
8
|
* These interfaces are engine-agnostic and define the contract
|
|
9
9
|
* for any layout engine implementation (ELK, dagre, etc.)
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Default layered layout options
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_LAYERED_OPTIONS = {
|
|
15
|
+
direction: 'DOWN',
|
|
16
|
+
layerSpacing: 50,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Default force layout options
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_FORCE_OPTIONS = {
|
|
22
|
+
iterations: 300,
|
|
23
|
+
repulsionStrength: 1,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Default stress layout options
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_STRESS_OPTIONS = {
|
|
29
|
+
iterations: 300,
|
|
30
|
+
edgeLengthFactor: 1,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Default radial layout options
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_RADIAL_OPTIONS = {
|
|
36
|
+
centerNodeId: undefined,
|
|
37
|
+
radiusIncrement: 100,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Default tree layout options
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_TREE_OPTIONS = {
|
|
43
|
+
direction: 'DOWN',
|
|
44
|
+
};
|
|
11
45
|
/**
|
|
12
46
|
* Default layout options
|
|
13
47
|
*/
|
|
14
48
|
const DEFAULT_LAYOUT_OPTIONS = {
|
|
15
49
|
algorithm: 'layered',
|
|
16
|
-
direction: 'DOWN',
|
|
17
50
|
nodeSpacing: 50,
|
|
18
|
-
layerSpacing: 50,
|
|
19
51
|
padding: 20,
|
|
20
52
|
considerEdges: true,
|
|
53
|
+
layered: DEFAULT_LAYERED_OPTIONS,
|
|
54
|
+
force: DEFAULT_FORCE_OPTIONS,
|
|
55
|
+
stress: DEFAULT_STRESS_OPTIONS,
|
|
56
|
+
radial: DEFAULT_RADIAL_OPTIONS,
|
|
57
|
+
tree: DEFAULT_TREE_OPTIONS,
|
|
21
58
|
};
|
|
22
59
|
|
|
23
60
|
/**
|
|
@@ -54,7 +91,7 @@ class ElkLayoutEngine {
|
|
|
54
91
|
this.elk = new ELK();
|
|
55
92
|
}
|
|
56
93
|
async calculateLayout(input) {
|
|
57
|
-
const options =
|
|
94
|
+
const options = input.options ?? {};
|
|
58
95
|
// Build ELK graph structure
|
|
59
96
|
const elkGraph = this.buildElkGraph(input, options);
|
|
60
97
|
// Calculate layout
|
|
@@ -89,9 +126,11 @@ class ElkLayoutEngine {
|
|
|
89
126
|
}
|
|
90
127
|
return elkNode;
|
|
91
128
|
});
|
|
92
|
-
// Build edges (handling bidirectional edges)
|
|
93
|
-
const
|
|
94
|
-
|
|
129
|
+
// Build edges (handling bidirectional edges and algorithm-specific hints)
|
|
130
|
+
const considerEdges = options.considerEdges ?? DEFAULT_LAYOUT_OPTIONS.considerEdges;
|
|
131
|
+
const algorithm = options.algorithm ?? DEFAULT_LAYOUT_OPTIONS.algorithm;
|
|
132
|
+
const edges = considerEdges
|
|
133
|
+
? this.buildElkEdges(input.edges, algorithm)
|
|
95
134
|
: [];
|
|
96
135
|
return {
|
|
97
136
|
id: 'root',
|
|
@@ -103,21 +142,31 @@ class ElkLayoutEngine {
|
|
|
103
142
|
/**
|
|
104
143
|
* Convert layout edges to ELK edges, expanding bidirectional edges
|
|
105
144
|
*/
|
|
106
|
-
buildElkEdges(edges) {
|
|
145
|
+
buildElkEdges(edges, algorithm) {
|
|
107
146
|
const elkEdges = [];
|
|
108
147
|
for (const edge of edges) {
|
|
109
|
-
|
|
110
|
-
elkEdges.push({
|
|
148
|
+
const elkEdge = {
|
|
111
149
|
id: edge.id,
|
|
112
150
|
sources: [edge.sourceId],
|
|
113
151
|
targets: [edge.targetId],
|
|
114
|
-
}
|
|
152
|
+
};
|
|
153
|
+
// Apply algorithm-specific edge hints (reserved for future use)
|
|
154
|
+
if (algorithm === 'layered' && edge.layered) {
|
|
155
|
+
elkEdge.layoutOptions = {};
|
|
156
|
+
// Handle edge priority
|
|
157
|
+
if (edge.layered.priority !== undefined) {
|
|
158
|
+
elkEdge.layoutOptions['elk.layered.priority.direction'] = String(edge.layered.priority);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Add the primary edge (source → target)
|
|
162
|
+
elkEdges.push(elkEdge);
|
|
115
163
|
// For bidirectional edges, add reverse edge (target → source)
|
|
116
164
|
if (edge.type === 'bidirectional') {
|
|
117
165
|
elkEdges.push({
|
|
118
166
|
id: `${edge.id}_reverse`,
|
|
119
167
|
sources: [edge.targetId],
|
|
120
168
|
targets: [edge.sourceId],
|
|
169
|
+
layoutOptions: elkEdge.layoutOptions,
|
|
121
170
|
});
|
|
122
171
|
}
|
|
123
172
|
}
|
|
@@ -127,35 +176,56 @@ class ElkLayoutEngine {
|
|
|
127
176
|
* Convert our abstract options to ELK-specific layout options
|
|
128
177
|
*/
|
|
129
178
|
buildElkOptions(options) {
|
|
179
|
+
const algorithm = options.algorithm ?? DEFAULT_LAYOUT_OPTIONS.algorithm;
|
|
180
|
+
const nodeSpacing = options.nodeSpacing ?? DEFAULT_LAYOUT_OPTIONS.nodeSpacing;
|
|
181
|
+
const padding = options.padding ?? DEFAULT_LAYOUT_OPTIONS.padding;
|
|
130
182
|
const elkOptions = {
|
|
131
|
-
'elk.algorithm': ELK_ALGORITHMS[
|
|
132
|
-
'elk.spacing.nodeNode': String(
|
|
133
|
-
'elk.padding': `[top=${
|
|
183
|
+
'elk.algorithm': ELK_ALGORITHMS[algorithm],
|
|
184
|
+
'elk.spacing.nodeNode': String(nodeSpacing),
|
|
185
|
+
'elk.padding': `[top=${padding}, left=${padding}, bottom=${padding}, right=${padding}]`,
|
|
134
186
|
};
|
|
135
187
|
// Algorithm-specific options
|
|
136
|
-
switch (
|
|
137
|
-
case 'layered':
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
elkOptions['elk.
|
|
188
|
+
switch (algorithm) {
|
|
189
|
+
case 'layered': {
|
|
190
|
+
const layeredOpts = options.layered ?? DEFAULT_LAYERED_OPTIONS;
|
|
191
|
+
const direction = layeredOpts.direction ?? DEFAULT_LAYERED_OPTIONS.direction;
|
|
192
|
+
const layerSpacing = layeredOpts.layerSpacing ?? DEFAULT_LAYERED_OPTIONS.layerSpacing;
|
|
193
|
+
elkOptions['elk.direction'] = ELK_DIRECTIONS[direction];
|
|
194
|
+
elkOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = String(layerSpacing);
|
|
195
|
+
elkOptions['elk.layered.spacing.edgeNodeBetweenLayers'] = String(layerSpacing / 2);
|
|
142
196
|
break;
|
|
143
|
-
|
|
144
|
-
|
|
197
|
+
}
|
|
198
|
+
case 'tree': {
|
|
199
|
+
const treeOpts = options.tree ?? DEFAULT_TREE_OPTIONS;
|
|
200
|
+
const direction = treeOpts.direction ?? DEFAULT_TREE_OPTIONS.direction;
|
|
201
|
+
elkOptions['elk.direction'] = ELK_DIRECTIONS[direction];
|
|
145
202
|
elkOptions['elk.mrtree.weighting'] = 'CONSTRAINT';
|
|
146
203
|
break;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
}
|
|
205
|
+
case 'force': {
|
|
206
|
+
const forceOpts = options.force ?? {};
|
|
207
|
+
const iterations = forceOpts.iterations ?? DEFAULT_FORCE_OPTIONS.iterations;
|
|
208
|
+
const repulsion = forceOpts.repulsionStrength ?? DEFAULT_FORCE_OPTIONS.repulsionStrength;
|
|
209
|
+
elkOptions['elk.force.iterations'] = String(iterations);
|
|
210
|
+
elkOptions['elk.force.repulsion'] = String(repulsion);
|
|
150
211
|
break;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
212
|
+
}
|
|
213
|
+
case 'stress': {
|
|
214
|
+
const stressOpts = options.stress ?? {};
|
|
215
|
+
const iterations = stressOpts.iterations ?? DEFAULT_STRESS_OPTIONS.iterations;
|
|
216
|
+
const edgeLengthFactor = stressOpts.edgeLengthFactor ?? DEFAULT_STRESS_OPTIONS.edgeLengthFactor;
|
|
217
|
+
elkOptions['elk.stress.desiredEdgeLength'] = String(nodeSpacing * 2 * edgeLengthFactor);
|
|
218
|
+
elkOptions['elk.stress.iterationLimit'] = String(iterations);
|
|
154
219
|
break;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
220
|
+
}
|
|
221
|
+
case 'radial': {
|
|
222
|
+
const radialOpts = options.radial ?? {};
|
|
223
|
+
const radiusIncrement = radialOpts.radiusIncrement ?? DEFAULT_RADIAL_OPTIONS.radiusIncrement;
|
|
224
|
+
elkOptions['elk.radial.radius'] = String(radiusIncrement);
|
|
225
|
+
// If a center node is specified, we could use it, but ELK's radial
|
|
226
|
+
// typically auto-detects the root
|
|
158
227
|
break;
|
|
228
|
+
}
|
|
159
229
|
}
|
|
160
230
|
return elkOptions;
|
|
161
231
|
}
|
|
@@ -425,5 +495,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
425
495
|
* Generated bundle index. Do not edit.
|
|
426
496
|
*/
|
|
427
497
|
|
|
428
|
-
export { DEFAULT_LAYOUT_OPTIONS, ElkLayoutEngine, LAYOUT_ENGINE, LayoutService, defaultLayoutEngineFactory };
|
|
498
|
+
export { DEFAULT_FORCE_OPTIONS, DEFAULT_LAYERED_OPTIONS, DEFAULT_LAYOUT_OPTIONS, DEFAULT_RADIAL_OPTIONS, DEFAULT_STRESS_OPTIONS, DEFAULT_TREE_OPTIONS, ElkLayoutEngine, LAYOUT_ENGINE, LayoutService, defaultLayoutEngineFactory };
|
|
429
499
|
//# sourceMappingURL=ngx-km-layout.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-km-layout.mjs","sources":["../../../../libs/ngx-km-layout/src/lib/models/layout.models.ts","../../../../libs/ngx-km-layout/src/lib/engines/elk-layout-engine.ts","../../../../libs/ngx-km-layout/src/lib/services/layout.service.ts","../../../../libs/ngx-km-layout/src/index.ts","../../../../libs/ngx-km-layout/src/ngx-km-layout.ts"],"sourcesContent":["/**\n * Layout Library Models\n *\n * These interfaces are engine-agnostic and define the contract\n * for any layout engine implementation (ELK, dagre, etc.)\n */\n\n/**\n * Direction for hierarchical/layered layouts\n */\nexport type LayoutDirection = 'DOWN' | 'UP' | 'RIGHT' | 'LEFT';\n\n/**\n * Available layout algorithm types\n * These are abstract algorithm categories that map to specific engine implementations\n */\nexport type LayoutAlgorithm =\n | 'layered' // Hierarchical/layered layout (Sugiyama-style)\n | 'force' // Force-directed layout\n | 'stress' // Stress-minimization layout\n | 'radial' // Radial tree layout\n | 'tree'; // Simple tree layout\n\n/**\n * Input node definition for layout calculation\n */\nexport interface LayoutNode {\n /** Unique identifier for the node */\n id: string;\n /** Width of the node in pixels */\n width: number;\n /** Height of the node in pixels */\n height: number;\n /** Optional: Fixed X position (node won't be moved if set) */\n fixedX?: number;\n /** Optional: Fixed Y position (node won't be moved if set) */\n fixedY?: number;\n}\n\n/**\n * Edge type for layout calculation\n * - 'directed': One-way relationship (source → target)\n * - 'bidirectional': Two-way relationship (source ↔ target)\n */\nexport type EdgeType = 'directed' | 'bidirectional';\n\n/**\n * Edge/relationship between nodes for layout calculation\n */\nexport interface LayoutEdge {\n /** Unique identifier for the edge */\n id: string;\n /** Source node ID */\n sourceId: string;\n /** Target node ID */\n targetId: string;\n /** Edge type (default: 'directed') */\n type?: EdgeType;\n}\n\n/**\n * Configuration options for layout calculation\n */\nexport interface LayoutOptions {\n /** Layout algorithm to use (default: 'layered') */\n algorithm?: LayoutAlgorithm;\n\n /** Direction for hierarchical layouts (default: 'DOWN') */\n direction?: LayoutDirection;\n\n /** Horizontal spacing between nodes in pixels (default: 50) */\n nodeSpacing?: number;\n\n /** Vertical spacing between layers in pixels (default: 50) */\n layerSpacing?: number;\n\n /** Padding around the entire graph in pixels (default: 20) */\n padding?: number;\n\n /** Whether edges should be considered for layout (default: true) */\n considerEdges?: boolean;\n}\n\n/**\n * Result of layout calculation for a single node\n */\nexport interface LayoutNodeResult {\n /** Node ID */\n id: string;\n /** Calculated X position (top-left corner) */\n x: number;\n /** Calculated Y position (top-left corner) */\n y: number;\n /** Node width (same as input) */\n width: number;\n /** Node height (same as input) */\n height: number;\n}\n\n/**\n * Complete result of layout calculation\n */\nexport interface LayoutResult {\n /** Positioned nodes */\n nodes: LayoutNodeResult[];\n /** Total width of the laid out graph */\n width: number;\n /** Total height of the laid out graph */\n height: number;\n}\n\n/**\n * Input data for layout calculation\n */\nexport interface LayoutInput {\n /** Nodes to layout */\n nodes: LayoutNode[];\n /** Edges/relationships between nodes */\n edges: LayoutEdge[];\n /** Layout options */\n options?: LayoutOptions;\n}\n\n/**\n * Default layout options\n */\nexport const DEFAULT_LAYOUT_OPTIONS: Required<LayoutOptions> = {\n algorithm: 'layered',\n direction: 'DOWN',\n nodeSpacing: 50,\n layerSpacing: 50,\n padding: 20,\n considerEdges: true,\n};\n","import ELK, { ElkNode, ElkExtendedEdge, LayoutOptions as ElkLayoutOptions } from 'elkjs/lib/elk.bundled.js';\nimport { LayoutEngine } from './layout-engine';\nimport {\n LayoutInput,\n LayoutResult,\n LayoutNodeResult,\n LayoutOptions,\n LayoutAlgorithm,\n LayoutDirection,\n LayoutEdge,\n DEFAULT_LAYOUT_OPTIONS,\n} from '../models/layout.models';\n\n/**\n * ELK algorithm identifiers\n * Maps our abstract algorithm types to ELK-specific algorithm IDs\n */\nconst ELK_ALGORITHMS: Record<LayoutAlgorithm, string> = {\n layered: 'layered',\n force: 'force',\n stress: 'stress',\n radial: 'radial',\n tree: 'mrtree',\n};\n\n/**\n * ELK direction values\n * Maps our direction type to ELK-specific direction values\n */\nconst ELK_DIRECTIONS: Record<LayoutDirection, string> = {\n DOWN: 'DOWN',\n UP: 'UP',\n RIGHT: 'RIGHT',\n LEFT: 'LEFT',\n};\n\n/**\n * ELK.js layout engine implementation\n *\n * Uses ELK.js (Eclipse Layout Kernel) for graph layout calculation.\n * This is a high-quality layout library that supports multiple algorithms.\n */\nexport class ElkLayoutEngine implements LayoutEngine {\n readonly name = 'ELK';\n\n private elk: InstanceType<typeof ELK>;\n\n constructor() {\n this.elk = new ELK();\n }\n\n async calculateLayout(input: LayoutInput): Promise<LayoutResult> {\n const options = { ...DEFAULT_LAYOUT_OPTIONS, ...input.options };\n\n // Build ELK graph structure\n const elkGraph = this.buildElkGraph(input, options);\n\n // Calculate layout\n const layoutedGraph = await this.elk.layout(elkGraph);\n\n // Extract results\n return this.extractResults(layoutedGraph);\n }\n\n dispose(): void {\n // ELK doesn't require explicit cleanup, but we provide\n // this method for consistency with the interface\n }\n\n /**\n * Convert our abstract input to ELK-specific graph structure\n */\n private buildElkGraph(\n input: LayoutInput,\n options: Required<LayoutOptions>\n ): ElkNode {\n const elkOptions = this.buildElkOptions(options);\n\n // Build nodes\n const children: ElkNode[] = input.nodes.map((node) => {\n const elkNode: ElkNode = {\n id: node.id,\n width: node.width,\n height: node.height,\n };\n\n // Handle fixed positions\n if (node.fixedX !== undefined && node.fixedY !== undefined) {\n elkNode.x = node.fixedX;\n elkNode.y = node.fixedY;\n // Mark as fixed in ELK\n elkNode.layoutOptions = {\n 'elk.position': `(${node.fixedX}, ${node.fixedY})`,\n };\n }\n\n return elkNode;\n });\n\n // Build edges (handling bidirectional edges)\n const edges: ElkExtendedEdge[] = options.considerEdges\n ? this.buildElkEdges(input.edges)\n : [];\n\n return {\n id: 'root',\n layoutOptions: elkOptions,\n children,\n edges,\n };\n }\n\n /**\n * Convert layout edges to ELK edges, expanding bidirectional edges\n */\n private buildElkEdges(edges: LayoutEdge[]): ElkExtendedEdge[] {\n const elkEdges: ElkExtendedEdge[] = [];\n\n for (const edge of edges) {\n // Add the primary edge (source → target)\n elkEdges.push({\n id: edge.id,\n sources: [edge.sourceId],\n targets: [edge.targetId],\n });\n\n // For bidirectional edges, add reverse edge (target → source)\n if (edge.type === 'bidirectional') {\n elkEdges.push({\n id: `${edge.id}_reverse`,\n sources: [edge.targetId],\n targets: [edge.sourceId],\n });\n }\n }\n\n return elkEdges;\n }\n\n /**\n * Convert our abstract options to ELK-specific layout options\n */\n private buildElkOptions(options: Required<LayoutOptions>): ElkLayoutOptions {\n const elkOptions: ElkLayoutOptions = {\n 'elk.algorithm': ELK_ALGORITHMS[options.algorithm],\n 'elk.spacing.nodeNode': String(options.nodeSpacing),\n 'elk.padding': `[top=${options.padding}, left=${options.padding}, bottom=${options.padding}, right=${options.padding}]`,\n };\n\n // Algorithm-specific options\n switch (options.algorithm) {\n case 'layered':\n elkOptions['elk.direction'] = ELK_DIRECTIONS[options.direction];\n elkOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = String(\n options.layerSpacing\n );\n // Improve edge routing\n elkOptions['elk.layered.spacing.edgeNodeBetweenLayers'] = String(\n options.layerSpacing / 2\n );\n break;\n\n case 'tree':\n elkOptions['elk.direction'] = ELK_DIRECTIONS[options.direction];\n elkOptions['elk.mrtree.weighting'] = 'CONSTRAINT';\n break;\n\n case 'force':\n // Force-directed doesn't use direction\n elkOptions['elk.force.iterations'] = '300';\n break;\n\n case 'stress':\n // Stress-based layout options\n elkOptions['elk.stress.desiredEdgeLength'] = String(\n options.nodeSpacing * 2\n );\n break;\n\n case 'radial':\n // Radial layout options\n elkOptions['elk.radial.radius'] = String(options.layerSpacing);\n break;\n }\n\n return elkOptions;\n }\n\n /**\n * Extract layout results from ELK's output\n */\n private extractResults(elkGraph: ElkNode): LayoutResult {\n const nodes: LayoutNodeResult[] = (elkGraph.children || []).map((child) => ({\n id: child.id,\n x: child.x ?? 0,\n y: child.y ?? 0,\n width: child.width ?? 0,\n height: child.height ?? 0,\n }));\n\n return {\n nodes,\n width: elkGraph.width ?? 0,\n height: elkGraph.height ?? 0,\n };\n }\n}\n","import { Injectable, InjectionToken, inject, OnDestroy } from '@angular/core';\nimport { LayoutEngine } from '../engines/layout-engine';\nimport { ElkLayoutEngine } from '../engines/elk-layout-engine';\nimport {\n LayoutInput,\n LayoutResult,\n LayoutNode,\n LayoutNodeResult,\n LayoutEdge,\n LayoutOptions,\n} from '../models/layout.models';\n\n/**\n * Injection token for providing a custom layout engine\n * Use this to swap the default ELK engine with a different implementation\n *\n * @example\n * ```typescript\n * providers: [\n * { provide: LAYOUT_ENGINE, useClass: CustomLayoutEngine }\n * ]\n * ```\n */\nexport const LAYOUT_ENGINE = new InjectionToken<LayoutEngine>('LAYOUT_ENGINE');\n\n/**\n * Factory function to create the default layout engine\n */\nexport function defaultLayoutEngineFactory(): LayoutEngine {\n return new ElkLayoutEngine();\n}\n\n/**\n * Layout Service\n *\n * Provides graph layout calculation using a pluggable layout engine.\n * By default uses ELK.js, but can be configured to use any engine\n * that implements the LayoutEngine interface.\n *\n * @example\n * ```typescript\n * // Basic usage\n * const result = await layoutService.calculateLayout({\n * nodes: [\n * { id: 'a', width: 100, height: 50 },\n * { id: 'b', width: 100, height: 50 },\n * ],\n * edges: [\n * { id: 'e1', sourceId: 'a', targetId: 'b' }\n * ],\n * options: { algorithm: 'layered', direction: 'DOWN' }\n * });\n * ```\n */\n@Injectable()\nexport class LayoutService implements OnDestroy {\n private engine: LayoutEngine;\n\n constructor() {\n // Try to inject a custom engine, fall back to default ELK engine\n const injectedEngine = inject(LAYOUT_ENGINE, { optional: true });\n this.engine = injectedEngine ?? defaultLayoutEngineFactory();\n }\n\n /**\n * Get the name of the current layout engine\n */\n get engineName(): string {\n return this.engine.name;\n }\n\n /**\n * Calculate layout positions for nodes based on their relationships\n *\n * @param input Layout input containing nodes, edges, and options\n * @returns Promise resolving to layout result with positioned nodes\n */\n async calculateLayout(input: LayoutInput): Promise<LayoutResult> {\n // Validate input\n this.validateInput(input);\n\n return this.engine.calculateLayout(input);\n }\n\n /**\n * Convenience method to calculate layout from separate parameters\n *\n * @param nodes Nodes to layout\n * @param edges Edges/relationships between nodes\n * @param options Layout options\n * @returns Promise resolving to layout result\n */\n async layout(\n nodes: LayoutNode[],\n edges: LayoutEdge[],\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n return this.calculateLayout({ nodes, edges, options });\n }\n\n /**\n * Calculate layout and return a map of node positions by ID\n * Useful for quick position lookups\n *\n * @param input Layout input\n * @returns Promise resolving to a Map of node ID to position\n */\n async calculateLayoutMap(\n input: LayoutInput\n ): Promise<Map<string, { x: number; y: number }>> {\n const result = await this.calculateLayout(input);\n\n const positionMap = new Map<string, { x: number; y: number }>();\n for (const node of result.nodes) {\n positionMap.set(node.id, { x: node.x, y: node.y });\n }\n\n return positionMap;\n }\n\n /**\n * Recalculate layout while preserving positions of existing nodes\n *\n * This is useful when adding new nodes to an existing layout.\n * Existing nodes will keep their positions (as fixed points),\n * and only new nodes will be positioned by the layout algorithm.\n *\n * @param nodes All nodes (existing + new)\n * @param edges All edges\n * @param existingPositions Map of existing node IDs to their positions\n * @param options Layout options\n * @returns Promise resolving to layout result\n *\n * @example\n * ```typescript\n * // Add a new node to existing layout\n * const newNodes = [...existingNodes, { id: 'new', width: 100, height: 50 }];\n * const result = await layoutService.recalculateLayout(\n * newNodes,\n * edges,\n * existingPositions, // Map from previous layout\n * options\n * );\n * ```\n */\n async recalculateLayout(\n nodes: LayoutNode[],\n edges: LayoutEdge[],\n existingPositions: Map<string, { x: number; y: number }>,\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n // Apply fixed positions to existing nodes\n const nodesWithFixedPositions = nodes.map((node) => {\n const existingPos = existingPositions.get(node.id);\n if (existingPos) {\n return {\n ...node,\n fixedX: existingPos.x,\n fixedY: existingPos.y,\n };\n }\n return node;\n });\n\n return this.calculateLayout({\n nodes: nodesWithFixedPositions,\n edges,\n options,\n });\n }\n\n /**\n * Calculate incremental layout for new nodes only\n *\n * Takes a previous layout result and adds new nodes to it.\n * Existing nodes keep their positions, new nodes are positioned.\n *\n * @param previousResult Previous layout result\n * @param newNodes New nodes to add\n * @param allEdges All edges (including edges for new nodes)\n * @param options Layout options\n * @returns Promise resolving to updated layout result\n */\n async addNodesToLayout(\n previousResult: LayoutResult,\n newNodes: LayoutNode[],\n allEdges: LayoutEdge[],\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n // Build position map from previous result\n const existingPositions = new Map<string, { x: number; y: number }>();\n for (const node of previousResult.nodes) {\n existingPositions.set(node.id, { x: node.x, y: node.y });\n }\n\n // Combine existing nodes (with dimensions) and new nodes\n const existingNodes: LayoutNode[] = previousResult.nodes.map((n) => ({\n id: n.id,\n width: n.width,\n height: n.height,\n }));\n\n const allNodes = [...existingNodes, ...newNodes];\n\n return this.recalculateLayout(allNodes, allEdges, existingPositions, options);\n }\n\n /**\n * Remove nodes from layout and recalculate\n *\n * Removes specified nodes and their connected edges,\n * then recalculates layout for remaining nodes.\n *\n * @param previousResult Previous layout result\n * @param nodeIdsToRemove IDs of nodes to remove\n * @param allEdges All edges (will be filtered to remove orphaned edges)\n * @param options Layout options\n * @param preservePositions If true, remaining nodes keep their positions\n * @returns Promise resolving to updated layout result\n */\n async removeNodesFromLayout(\n previousResult: LayoutResult,\n nodeIdsToRemove: string[],\n allEdges: LayoutEdge[],\n options?: LayoutOptions,\n preservePositions = true\n ): Promise<LayoutResult> {\n const removeSet = new Set(nodeIdsToRemove);\n\n // Filter out removed nodes\n const remainingNodes: LayoutNode[] = previousResult.nodes\n .filter((n) => !removeSet.has(n.id))\n .map((n) => ({\n id: n.id,\n width: n.width,\n height: n.height,\n }));\n\n // Filter out edges connected to removed nodes\n const remainingEdges = allEdges.filter(\n (e) => !removeSet.has(e.sourceId) && !removeSet.has(e.targetId)\n );\n\n if (remainingNodes.length === 0) {\n return { nodes: [], width: 0, height: 0 };\n }\n\n if (preservePositions) {\n const existingPositions = new Map<string, { x: number; y: number }>();\n for (const node of previousResult.nodes) {\n if (!removeSet.has(node.id)) {\n existingPositions.set(node.id, { x: node.x, y: node.y });\n }\n }\n return this.recalculateLayout(\n remainingNodes,\n remainingEdges,\n existingPositions,\n options\n );\n }\n\n return this.calculateLayout({\n nodes: remainingNodes,\n edges: remainingEdges,\n options,\n });\n }\n\n ngOnDestroy(): void {\n this.engine.dispose?.();\n }\n\n /**\n * Validate layout input\n */\n private validateInput(input: LayoutInput): void {\n if (!input.nodes || input.nodes.length === 0) {\n throw new Error('LayoutService: At least one node is required');\n }\n\n // Check for duplicate node IDs\n const nodeIds = new Set<string>();\n for (const node of input.nodes) {\n if (nodeIds.has(node.id)) {\n throw new Error(`LayoutService: Duplicate node ID \"${node.id}\"`);\n }\n nodeIds.add(node.id);\n }\n\n // Validate edges reference existing nodes\n if (input.edges) {\n for (const edge of input.edges) {\n if (!nodeIds.has(edge.sourceId)) {\n throw new Error(\n `LayoutService: Edge \"${edge.id}\" references unknown source node \"${edge.sourceId}\"`\n );\n }\n if (!nodeIds.has(edge.targetId)) {\n throw new Error(\n `LayoutService: Edge \"${edge.id}\" references unknown target node \"${edge.targetId}\"`\n );\n }\n }\n }\n }\n}\n","// Models - Types\nexport type {\n LayoutDirection,\n LayoutAlgorithm,\n EdgeType,\n LayoutNode,\n LayoutEdge,\n LayoutOptions,\n LayoutNodeResult,\n LayoutResult,\n LayoutInput,\n} from './lib/models/layout.models';\n\n// Models - Values\nexport { DEFAULT_LAYOUT_OPTIONS } from './lib/models/layout.models';\n\n// Engine abstraction (for custom engine implementations)\nexport type { LayoutEngine } from './lib/engines/layout-engine';\nexport { ElkLayoutEngine } from './lib/engines/elk-layout-engine';\n\n// Service\nexport {\n LayoutService,\n LAYOUT_ENGINE,\n defaultLayoutEngineFactory,\n} from './lib/services/layout.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-km-layout.mjs","sources":["../../../../libs/ngx-km-layout/src/lib/models/layout.models.ts","../../../../libs/ngx-km-layout/src/lib/engines/elk-layout-engine.ts","../../../../libs/ngx-km-layout/src/lib/services/layout.service.ts","../../../../libs/ngx-km-layout/src/index.ts","../../../../libs/ngx-km-layout/src/ngx-km-layout.ts"],"sourcesContent":["/**\n * Layout Library Models\n *\n * These interfaces are engine-agnostic and define the contract\n * for any layout engine implementation (ELK, dagre, etc.)\n */\n\n/**\n * Direction for hierarchical/layered layouts\n */\nexport type LayoutDirection = 'DOWN' | 'UP' | 'RIGHT' | 'LEFT';\n\n/**\n * Available layout algorithm types\n * These are abstract algorithm categories that map to specific engine implementations\n */\nexport type LayoutAlgorithm =\n | 'layered' // Hierarchical/layered layout (Sugiyama-style)\n | 'force' // Force-directed layout\n | 'stress' // Stress-minimization layout\n | 'radial' // Radial tree layout\n | 'tree'; // Simple tree layout\n\n// ============================================================================\n// Algorithm-Specific Options\n// ============================================================================\n\n/**\n * Options specific to layered (hierarchical) layout algorithm\n */\nexport interface LayeredLayoutOptions {\n /** Layout direction (default: 'DOWN') */\n direction?: LayoutDirection;\n /** Spacing between layers in pixels (default: 50) */\n layerSpacing?: number;\n}\n\n/**\n * Options specific to force-directed layout algorithm\n */\nexport interface ForceLayoutOptions {\n /** Number of iterations for force simulation (default: 300) */\n iterations?: number;\n /** Repulsion strength between nodes (default: 1) */\n repulsionStrength?: number;\n}\n\n/**\n * Options specific to stress layout algorithm\n */\nexport interface StressLayoutOptions {\n /** Number of iterations for stress minimization (default: 300) */\n iterations?: number;\n /** Desired edge length factor (default: 1) */\n edgeLengthFactor?: number;\n}\n\n/**\n * Options specific to radial layout algorithm\n */\nexport interface RadialLayoutOptions {\n /** ID of the center node (if not set, root is auto-detected) */\n centerNodeId?: string;\n /** Distance between radial layers (default: 100) */\n radiusIncrement?: number;\n}\n\n/**\n * Options specific to tree layout algorithm\n */\nexport interface TreeLayoutOptions {\n /** Layout direction (default: 'DOWN') */\n direction?: LayoutDirection;\n}\n\n// ============================================================================\n// Algorithm-Specific Edge Hints\n// ============================================================================\n\n/**\n * Edge type hint for layered layout\n * - 'hierarchical': Creates parent-child layer relationship (default)\n * - 'association': Hints that connected nodes should be on same layer\n */\nexport type LayeredEdgeType = 'hierarchical' | 'association';\n\n/**\n * Edge hints specific to layered layout\n */\nexport interface LayeredEdgeHint {\n /** Edge type affecting layer assignment (default: 'hierarchical') */\n type?: LayeredEdgeType;\n /** Priority for edge routing (higher = preferred) */\n priority?: number;\n}\n\n/**\n * Edge hints specific to force layout\n */\nexport interface ForceEdgeHint {\n /** Custom edge length for this specific edge */\n idealLength?: number;\n /** Strength of this edge's attraction (default: 1) */\n strength?: number;\n}\n\n/**\n * Input node definition for layout calculation\n */\nexport interface LayoutNode {\n /** Unique identifier for the node */\n id: string;\n /** Width of the node in pixels */\n width: number;\n /** Height of the node in pixels */\n height: number;\n /** Optional: Fixed X position (node won't be moved if set) */\n fixedX?: number;\n /** Optional: Fixed Y position (node won't be moved if set) */\n fixedY?: number;\n}\n\n/**\n * Edge type for layout calculation\n * - 'directed': One-way relationship (source → target)\n * - 'bidirectional': Two-way relationship (source ↔ target)\n */\nexport type EdgeType = 'directed' | 'bidirectional';\n\n/**\n * Edge/relationship between nodes for layout calculation\n */\nexport interface LayoutEdge {\n /** Unique identifier for the edge */\n id: string;\n /** Source node ID */\n sourceId: string;\n /** Target node ID */\n targetId: string;\n /** Edge type (default: 'directed') */\n type?: EdgeType;\n /** Hints for layered layout algorithm */\n layered?: LayeredEdgeHint;\n /** Hints for force layout algorithm */\n force?: ForceEdgeHint;\n}\n\n/**\n * Configuration options for layout calculation\n */\nexport interface LayoutOptions {\n /** Layout algorithm to use (default: 'layered') */\n algorithm?: LayoutAlgorithm;\n\n // ---- Common options (apply to all algorithms) ----\n\n /** Spacing between nodes in pixels (default: 50) */\n nodeSpacing?: number;\n\n /** Padding around the entire graph in pixels (default: 20) */\n padding?: number;\n\n /** Whether edges should be considered for layout (default: true) */\n considerEdges?: boolean;\n\n // ---- Algorithm-specific options ----\n\n /** Options for layered (hierarchical) layout */\n layered?: LayeredLayoutOptions;\n\n /** Options for force-directed layout */\n force?: ForceLayoutOptions;\n\n /** Options for stress layout */\n stress?: StressLayoutOptions;\n\n /** Options for radial layout */\n radial?: RadialLayoutOptions;\n\n /** Options for tree layout */\n tree?: TreeLayoutOptions;\n}\n\n/**\n * Result of layout calculation for a single node\n */\nexport interface LayoutNodeResult {\n /** Node ID */\n id: string;\n /** Calculated X position (top-left corner) */\n x: number;\n /** Calculated Y position (top-left corner) */\n y: number;\n /** Node width (same as input) */\n width: number;\n /** Node height (same as input) */\n height: number;\n}\n\n/**\n * Complete result of layout calculation\n */\nexport interface LayoutResult {\n /** Positioned nodes */\n nodes: LayoutNodeResult[];\n /** Total width of the laid out graph */\n width: number;\n /** Total height of the laid out graph */\n height: number;\n}\n\n/**\n * Input data for layout calculation\n */\nexport interface LayoutInput {\n /** Nodes to layout */\n nodes: LayoutNode[];\n /** Edges/relationships between nodes */\n edges: LayoutEdge[];\n /** Layout options */\n options?: LayoutOptions;\n}\n\n/**\n * Default layered layout options\n */\nexport const DEFAULT_LAYERED_OPTIONS: Required<LayeredLayoutOptions> = {\n direction: 'DOWN',\n layerSpacing: 50,\n};\n\n/**\n * Default force layout options\n */\nexport const DEFAULT_FORCE_OPTIONS: Required<ForceLayoutOptions> = {\n iterations: 300,\n repulsionStrength: 1,\n};\n\n/**\n * Default stress layout options\n */\nexport const DEFAULT_STRESS_OPTIONS: Required<StressLayoutOptions> = {\n iterations: 300,\n edgeLengthFactor: 1,\n};\n\n/**\n * Default radial layout options\n */\nexport const DEFAULT_RADIAL_OPTIONS: Omit<Required<RadialLayoutOptions>, 'centerNodeId'> & { centerNodeId?: string } = {\n centerNodeId: undefined,\n radiusIncrement: 100,\n};\n\n/**\n * Default tree layout options\n */\nexport const DEFAULT_TREE_OPTIONS: Required<TreeLayoutOptions> = {\n direction: 'DOWN',\n};\n\n/**\n * Default layout options\n */\nexport const DEFAULT_LAYOUT_OPTIONS = {\n algorithm: 'layered' as LayoutAlgorithm,\n nodeSpacing: 50,\n padding: 20,\n considerEdges: true,\n layered: DEFAULT_LAYERED_OPTIONS,\n force: DEFAULT_FORCE_OPTIONS,\n stress: DEFAULT_STRESS_OPTIONS,\n radial: DEFAULT_RADIAL_OPTIONS,\n tree: DEFAULT_TREE_OPTIONS,\n};\n","import ELK, { ElkNode, ElkExtendedEdge, LayoutOptions as ElkLayoutOptions } from 'elkjs/lib/elk.bundled.js';\nimport { LayoutEngine } from './layout-engine';\nimport {\n LayoutInput,\n LayoutResult,\n LayoutNodeResult,\n LayoutOptions,\n LayoutAlgorithm,\n LayoutDirection,\n LayoutEdge,\n DEFAULT_LAYOUT_OPTIONS,\n DEFAULT_LAYERED_OPTIONS,\n DEFAULT_FORCE_OPTIONS,\n DEFAULT_STRESS_OPTIONS,\n DEFAULT_RADIAL_OPTIONS,\n DEFAULT_TREE_OPTIONS,\n} from '../models/layout.models';\n\n/**\n * ELK algorithm identifiers\n * Maps our abstract algorithm types to ELK-specific algorithm IDs\n */\nconst ELK_ALGORITHMS: Record<LayoutAlgorithm, string> = {\n layered: 'layered',\n force: 'force',\n stress: 'stress',\n radial: 'radial',\n tree: 'mrtree',\n};\n\n/**\n * ELK direction values\n * Maps our direction type to ELK-specific direction values\n */\nconst ELK_DIRECTIONS: Record<LayoutDirection, string> = {\n DOWN: 'DOWN',\n UP: 'UP',\n RIGHT: 'RIGHT',\n LEFT: 'LEFT',\n};\n\n/**\n * ELK.js layout engine implementation\n *\n * Uses ELK.js (Eclipse Layout Kernel) for graph layout calculation.\n * This is a high-quality layout library that supports multiple algorithms.\n */\nexport class ElkLayoutEngine implements LayoutEngine {\n readonly name = 'ELK';\n\n private elk: InstanceType<typeof ELK>;\n\n constructor() {\n this.elk = new ELK();\n }\n\n async calculateLayout(input: LayoutInput): Promise<LayoutResult> {\n const options = input.options ?? {};\n\n // Build ELK graph structure\n const elkGraph = this.buildElkGraph(input, options);\n\n // Calculate layout\n const layoutedGraph = await this.elk.layout(elkGraph);\n\n // Extract results\n return this.extractResults(layoutedGraph);\n }\n\n dispose(): void {\n // ELK doesn't require explicit cleanup, but we provide\n // this method for consistency with the interface\n }\n\n /**\n * Convert our abstract input to ELK-specific graph structure\n */\n private buildElkGraph(\n input: LayoutInput,\n options: LayoutOptions\n ): ElkNode {\n const elkOptions = this.buildElkOptions(options);\n\n // Build nodes\n const children: ElkNode[] = input.nodes.map((node) => {\n const elkNode: ElkNode = {\n id: node.id,\n width: node.width,\n height: node.height,\n };\n\n // Handle fixed positions\n if (node.fixedX !== undefined && node.fixedY !== undefined) {\n elkNode.x = node.fixedX;\n elkNode.y = node.fixedY;\n // Mark as fixed in ELK\n elkNode.layoutOptions = {\n 'elk.position': `(${node.fixedX}, ${node.fixedY})`,\n };\n }\n\n return elkNode;\n });\n\n // Build edges (handling bidirectional edges and algorithm-specific hints)\n const considerEdges = options.considerEdges ?? DEFAULT_LAYOUT_OPTIONS.considerEdges;\n const algorithm = options.algorithm ?? DEFAULT_LAYOUT_OPTIONS.algorithm;\n const edges: ElkExtendedEdge[] = considerEdges\n ? this.buildElkEdges(input.edges, algorithm)\n : [];\n\n return {\n id: 'root',\n layoutOptions: elkOptions,\n children,\n edges,\n };\n }\n\n /**\n * Convert layout edges to ELK edges, expanding bidirectional edges\n */\n private buildElkEdges(edges: LayoutEdge[], algorithm: LayoutAlgorithm): ElkExtendedEdge[] {\n const elkEdges: ElkExtendedEdge[] = [];\n\n for (const edge of edges) {\n const elkEdge: ElkExtendedEdge = {\n id: edge.id,\n sources: [edge.sourceId],\n targets: [edge.targetId],\n };\n\n // Apply algorithm-specific edge hints (reserved for future use)\n if (algorithm === 'layered' && edge.layered) {\n elkEdge.layoutOptions = {};\n\n // Handle edge priority\n if (edge.layered.priority !== undefined) {\n elkEdge.layoutOptions['elk.layered.priority.direction'] = String(edge.layered.priority);\n }\n }\n\n // Add the primary edge (source → target)\n elkEdges.push(elkEdge);\n\n // For bidirectional edges, add reverse edge (target → source)\n if (edge.type === 'bidirectional') {\n elkEdges.push({\n id: `${edge.id}_reverse`,\n sources: [edge.targetId],\n targets: [edge.sourceId],\n layoutOptions: elkEdge.layoutOptions,\n });\n }\n }\n\n return elkEdges;\n }\n\n /**\n * Convert our abstract options to ELK-specific layout options\n */\n private buildElkOptions(options: LayoutOptions): ElkLayoutOptions {\n const algorithm = options.algorithm ?? DEFAULT_LAYOUT_OPTIONS.algorithm;\n const nodeSpacing = options.nodeSpacing ?? DEFAULT_LAYOUT_OPTIONS.nodeSpacing;\n const padding = options.padding ?? DEFAULT_LAYOUT_OPTIONS.padding;\n\n const elkOptions: ElkLayoutOptions = {\n 'elk.algorithm': ELK_ALGORITHMS[algorithm],\n 'elk.spacing.nodeNode': String(nodeSpacing),\n 'elk.padding': `[top=${padding}, left=${padding}, bottom=${padding}, right=${padding}]`,\n };\n\n // Algorithm-specific options\n switch (algorithm) {\n case 'layered': {\n const layeredOpts = options.layered ?? DEFAULT_LAYERED_OPTIONS;\n const direction = layeredOpts.direction ?? DEFAULT_LAYERED_OPTIONS.direction;\n const layerSpacing = layeredOpts.layerSpacing ?? DEFAULT_LAYERED_OPTIONS.layerSpacing;\n\n elkOptions['elk.direction'] = ELK_DIRECTIONS[direction];\n elkOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = String(layerSpacing);\n elkOptions['elk.layered.spacing.edgeNodeBetweenLayers'] = String(layerSpacing / 2);\n break;\n }\n\n case 'tree': {\n const treeOpts = options.tree ?? DEFAULT_TREE_OPTIONS;\n const direction = treeOpts.direction ?? DEFAULT_TREE_OPTIONS.direction;\n\n elkOptions['elk.direction'] = ELK_DIRECTIONS[direction];\n elkOptions['elk.mrtree.weighting'] = 'CONSTRAINT';\n break;\n }\n\n case 'force': {\n const forceOpts = options.force ?? {};\n const iterations = forceOpts.iterations ?? DEFAULT_FORCE_OPTIONS.iterations;\n const repulsion = forceOpts.repulsionStrength ?? DEFAULT_FORCE_OPTIONS.repulsionStrength;\n\n elkOptions['elk.force.iterations'] = String(iterations);\n elkOptions['elk.force.repulsion'] = String(repulsion);\n break;\n }\n\n case 'stress': {\n const stressOpts = options.stress ?? {};\n const iterations = stressOpts.iterations ?? DEFAULT_STRESS_OPTIONS.iterations;\n const edgeLengthFactor = stressOpts.edgeLengthFactor ?? DEFAULT_STRESS_OPTIONS.edgeLengthFactor;\n\n elkOptions['elk.stress.desiredEdgeLength'] = String(nodeSpacing * 2 * edgeLengthFactor);\n elkOptions['elk.stress.iterationLimit'] = String(iterations);\n break;\n }\n\n case 'radial': {\n const radialOpts = options.radial ?? {};\n const radiusIncrement = radialOpts.radiusIncrement ?? DEFAULT_RADIAL_OPTIONS.radiusIncrement;\n\n elkOptions['elk.radial.radius'] = String(radiusIncrement);\n // If a center node is specified, we could use it, but ELK's radial\n // typically auto-detects the root\n break;\n }\n }\n\n return elkOptions;\n }\n\n /**\n * Extract layout results from ELK's output\n */\n private extractResults(elkGraph: ElkNode): LayoutResult {\n const nodes: LayoutNodeResult[] = (elkGraph.children || []).map((child) => ({\n id: child.id,\n x: child.x ?? 0,\n y: child.y ?? 0,\n width: child.width ?? 0,\n height: child.height ?? 0,\n }));\n\n return {\n nodes,\n width: elkGraph.width ?? 0,\n height: elkGraph.height ?? 0,\n };\n }\n}\n","import { Injectable, InjectionToken, inject, OnDestroy } from '@angular/core';\nimport { LayoutEngine } from '../engines/layout-engine';\nimport { ElkLayoutEngine } from '../engines/elk-layout-engine';\nimport {\n LayoutInput,\n LayoutResult,\n LayoutNode,\n LayoutNodeResult,\n LayoutEdge,\n LayoutOptions,\n} from '../models/layout.models';\n\n/**\n * Injection token for providing a custom layout engine\n * Use this to swap the default ELK engine with a different implementation\n *\n * @example\n * ```typescript\n * providers: [\n * { provide: LAYOUT_ENGINE, useClass: CustomLayoutEngine }\n * ]\n * ```\n */\nexport const LAYOUT_ENGINE = new InjectionToken<LayoutEngine>('LAYOUT_ENGINE');\n\n/**\n * Factory function to create the default layout engine\n */\nexport function defaultLayoutEngineFactory(): LayoutEngine {\n return new ElkLayoutEngine();\n}\n\n/**\n * Layout Service\n *\n * Provides graph layout calculation using a pluggable layout engine.\n * By default uses ELK.js, but can be configured to use any engine\n * that implements the LayoutEngine interface.\n *\n * @example\n * ```typescript\n * // Basic usage\n * const result = await layoutService.calculateLayout({\n * nodes: [\n * { id: 'a', width: 100, height: 50 },\n * { id: 'b', width: 100, height: 50 },\n * ],\n * edges: [\n * { id: 'e1', sourceId: 'a', targetId: 'b' }\n * ],\n * options: { algorithm: 'layered', direction: 'DOWN' }\n * });\n * ```\n */\n@Injectable()\nexport class LayoutService implements OnDestroy {\n private engine: LayoutEngine;\n\n constructor() {\n // Try to inject a custom engine, fall back to default ELK engine\n const injectedEngine = inject(LAYOUT_ENGINE, { optional: true });\n this.engine = injectedEngine ?? defaultLayoutEngineFactory();\n }\n\n /**\n * Get the name of the current layout engine\n */\n get engineName(): string {\n return this.engine.name;\n }\n\n /**\n * Calculate layout positions for nodes based on their relationships\n *\n * @param input Layout input containing nodes, edges, and options\n * @returns Promise resolving to layout result with positioned nodes\n */\n async calculateLayout(input: LayoutInput): Promise<LayoutResult> {\n // Validate input\n this.validateInput(input);\n\n return this.engine.calculateLayout(input);\n }\n\n /**\n * Convenience method to calculate layout from separate parameters\n *\n * @param nodes Nodes to layout\n * @param edges Edges/relationships between nodes\n * @param options Layout options\n * @returns Promise resolving to layout result\n */\n async layout(\n nodes: LayoutNode[],\n edges: LayoutEdge[],\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n return this.calculateLayout({ nodes, edges, options });\n }\n\n /**\n * Calculate layout and return a map of node positions by ID\n * Useful for quick position lookups\n *\n * @param input Layout input\n * @returns Promise resolving to a Map of node ID to position\n */\n async calculateLayoutMap(\n input: LayoutInput\n ): Promise<Map<string, { x: number; y: number }>> {\n const result = await this.calculateLayout(input);\n\n const positionMap = new Map<string, { x: number; y: number }>();\n for (const node of result.nodes) {\n positionMap.set(node.id, { x: node.x, y: node.y });\n }\n\n return positionMap;\n }\n\n /**\n * Recalculate layout while preserving positions of existing nodes\n *\n * This is useful when adding new nodes to an existing layout.\n * Existing nodes will keep their positions (as fixed points),\n * and only new nodes will be positioned by the layout algorithm.\n *\n * @param nodes All nodes (existing + new)\n * @param edges All edges\n * @param existingPositions Map of existing node IDs to their positions\n * @param options Layout options\n * @returns Promise resolving to layout result\n *\n * @example\n * ```typescript\n * // Add a new node to existing layout\n * const newNodes = [...existingNodes, { id: 'new', width: 100, height: 50 }];\n * const result = await layoutService.recalculateLayout(\n * newNodes,\n * edges,\n * existingPositions, // Map from previous layout\n * options\n * );\n * ```\n */\n async recalculateLayout(\n nodes: LayoutNode[],\n edges: LayoutEdge[],\n existingPositions: Map<string, { x: number; y: number }>,\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n // Apply fixed positions to existing nodes\n const nodesWithFixedPositions = nodes.map((node) => {\n const existingPos = existingPositions.get(node.id);\n if (existingPos) {\n return {\n ...node,\n fixedX: existingPos.x,\n fixedY: existingPos.y,\n };\n }\n return node;\n });\n\n return this.calculateLayout({\n nodes: nodesWithFixedPositions,\n edges,\n options,\n });\n }\n\n /**\n * Calculate incremental layout for new nodes only\n *\n * Takes a previous layout result and adds new nodes to it.\n * Existing nodes keep their positions, new nodes are positioned.\n *\n * @param previousResult Previous layout result\n * @param newNodes New nodes to add\n * @param allEdges All edges (including edges for new nodes)\n * @param options Layout options\n * @returns Promise resolving to updated layout result\n */\n async addNodesToLayout(\n previousResult: LayoutResult,\n newNodes: LayoutNode[],\n allEdges: LayoutEdge[],\n options?: LayoutOptions\n ): Promise<LayoutResult> {\n // Build position map from previous result\n const existingPositions = new Map<string, { x: number; y: number }>();\n for (const node of previousResult.nodes) {\n existingPositions.set(node.id, { x: node.x, y: node.y });\n }\n\n // Combine existing nodes (with dimensions) and new nodes\n const existingNodes: LayoutNode[] = previousResult.nodes.map((n) => ({\n id: n.id,\n width: n.width,\n height: n.height,\n }));\n\n const allNodes = [...existingNodes, ...newNodes];\n\n return this.recalculateLayout(allNodes, allEdges, existingPositions, options);\n }\n\n /**\n * Remove nodes from layout and recalculate\n *\n * Removes specified nodes and their connected edges,\n * then recalculates layout for remaining nodes.\n *\n * @param previousResult Previous layout result\n * @param nodeIdsToRemove IDs of nodes to remove\n * @param allEdges All edges (will be filtered to remove orphaned edges)\n * @param options Layout options\n * @param preservePositions If true, remaining nodes keep their positions\n * @returns Promise resolving to updated layout result\n */\n async removeNodesFromLayout(\n previousResult: LayoutResult,\n nodeIdsToRemove: string[],\n allEdges: LayoutEdge[],\n options?: LayoutOptions,\n preservePositions = true\n ): Promise<LayoutResult> {\n const removeSet = new Set(nodeIdsToRemove);\n\n // Filter out removed nodes\n const remainingNodes: LayoutNode[] = previousResult.nodes\n .filter((n) => !removeSet.has(n.id))\n .map((n) => ({\n id: n.id,\n width: n.width,\n height: n.height,\n }));\n\n // Filter out edges connected to removed nodes\n const remainingEdges = allEdges.filter(\n (e) => !removeSet.has(e.sourceId) && !removeSet.has(e.targetId)\n );\n\n if (remainingNodes.length === 0) {\n return { nodes: [], width: 0, height: 0 };\n }\n\n if (preservePositions) {\n const existingPositions = new Map<string, { x: number; y: number }>();\n for (const node of previousResult.nodes) {\n if (!removeSet.has(node.id)) {\n existingPositions.set(node.id, { x: node.x, y: node.y });\n }\n }\n return this.recalculateLayout(\n remainingNodes,\n remainingEdges,\n existingPositions,\n options\n );\n }\n\n return this.calculateLayout({\n nodes: remainingNodes,\n edges: remainingEdges,\n options,\n });\n }\n\n ngOnDestroy(): void {\n this.engine.dispose?.();\n }\n\n /**\n * Validate layout input\n */\n private validateInput(input: LayoutInput): void {\n if (!input.nodes || input.nodes.length === 0) {\n throw new Error('LayoutService: At least one node is required');\n }\n\n // Check for duplicate node IDs\n const nodeIds = new Set<string>();\n for (const node of input.nodes) {\n if (nodeIds.has(node.id)) {\n throw new Error(`LayoutService: Duplicate node ID \"${node.id}\"`);\n }\n nodeIds.add(node.id);\n }\n\n // Validate edges reference existing nodes\n if (input.edges) {\n for (const edge of input.edges) {\n if (!nodeIds.has(edge.sourceId)) {\n throw new Error(\n `LayoutService: Edge \"${edge.id}\" references unknown source node \"${edge.sourceId}\"`\n );\n }\n if (!nodeIds.has(edge.targetId)) {\n throw new Error(\n `LayoutService: Edge \"${edge.id}\" references unknown target node \"${edge.targetId}\"`\n );\n }\n }\n }\n }\n}\n","// Models - Types\nexport type {\n LayoutDirection,\n LayoutAlgorithm,\n EdgeType,\n LayoutNode,\n LayoutEdge,\n LayoutOptions,\n LayoutNodeResult,\n LayoutResult,\n LayoutInput,\n // Algorithm-specific options\n LayeredLayoutOptions,\n ForceLayoutOptions,\n StressLayoutOptions,\n RadialLayoutOptions,\n TreeLayoutOptions,\n // Edge hints\n LayeredEdgeType,\n LayeredEdgeHint,\n ForceEdgeHint,\n} from './lib/models/layout.models';\n\n// Models - Values\nexport {\n DEFAULT_LAYOUT_OPTIONS,\n DEFAULT_LAYERED_OPTIONS,\n DEFAULT_FORCE_OPTIONS,\n DEFAULT_STRESS_OPTIONS,\n DEFAULT_RADIAL_OPTIONS,\n DEFAULT_TREE_OPTIONS,\n} from './lib/models/layout.models';\n\n// Engine abstraction (for custom engine implementations)\nexport type { LayoutEngine } from './lib/engines/layout-engine';\nexport { ElkLayoutEngine } from './lib/engines/elk-layout-engine';\n\n// Service\nexport {\n LayoutService,\n LAYOUT_ENGINE,\n defaultLayoutEngineFactory,\n} from './lib/services/layout.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -16,6 +16,73 @@ type LayoutDirection = 'DOWN' | 'UP' | 'RIGHT' | 'LEFT';
|
|
|
16
16
|
* These are abstract algorithm categories that map to specific engine implementations
|
|
17
17
|
*/
|
|
18
18
|
type LayoutAlgorithm = 'layered' | 'force' | 'stress' | 'radial' | 'tree';
|
|
19
|
+
/**
|
|
20
|
+
* Options specific to layered (hierarchical) layout algorithm
|
|
21
|
+
*/
|
|
22
|
+
interface LayeredLayoutOptions {
|
|
23
|
+
/** Layout direction (default: 'DOWN') */
|
|
24
|
+
direction?: LayoutDirection;
|
|
25
|
+
/** Spacing between layers in pixels (default: 50) */
|
|
26
|
+
layerSpacing?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Options specific to force-directed layout algorithm
|
|
30
|
+
*/
|
|
31
|
+
interface ForceLayoutOptions {
|
|
32
|
+
/** Number of iterations for force simulation (default: 300) */
|
|
33
|
+
iterations?: number;
|
|
34
|
+
/** Repulsion strength between nodes (default: 1) */
|
|
35
|
+
repulsionStrength?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Options specific to stress layout algorithm
|
|
39
|
+
*/
|
|
40
|
+
interface StressLayoutOptions {
|
|
41
|
+
/** Number of iterations for stress minimization (default: 300) */
|
|
42
|
+
iterations?: number;
|
|
43
|
+
/** Desired edge length factor (default: 1) */
|
|
44
|
+
edgeLengthFactor?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Options specific to radial layout algorithm
|
|
48
|
+
*/
|
|
49
|
+
interface RadialLayoutOptions {
|
|
50
|
+
/** ID of the center node (if not set, root is auto-detected) */
|
|
51
|
+
centerNodeId?: string;
|
|
52
|
+
/** Distance between radial layers (default: 100) */
|
|
53
|
+
radiusIncrement?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Options specific to tree layout algorithm
|
|
57
|
+
*/
|
|
58
|
+
interface TreeLayoutOptions {
|
|
59
|
+
/** Layout direction (default: 'DOWN') */
|
|
60
|
+
direction?: LayoutDirection;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Edge type hint for layered layout
|
|
64
|
+
* - 'hierarchical': Creates parent-child layer relationship (default)
|
|
65
|
+
* - 'association': Hints that connected nodes should be on same layer
|
|
66
|
+
*/
|
|
67
|
+
type LayeredEdgeType = 'hierarchical' | 'association';
|
|
68
|
+
/**
|
|
69
|
+
* Edge hints specific to layered layout
|
|
70
|
+
*/
|
|
71
|
+
interface LayeredEdgeHint {
|
|
72
|
+
/** Edge type affecting layer assignment (default: 'hierarchical') */
|
|
73
|
+
type?: LayeredEdgeType;
|
|
74
|
+
/** Priority for edge routing (higher = preferred) */
|
|
75
|
+
priority?: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Edge hints specific to force layout
|
|
79
|
+
*/
|
|
80
|
+
interface ForceEdgeHint {
|
|
81
|
+
/** Custom edge length for this specific edge */
|
|
82
|
+
idealLength?: number;
|
|
83
|
+
/** Strength of this edge's attraction (default: 1) */
|
|
84
|
+
strength?: number;
|
|
85
|
+
}
|
|
19
86
|
/**
|
|
20
87
|
* Input node definition for layout calculation
|
|
21
88
|
*/
|
|
@@ -49,6 +116,10 @@ interface LayoutEdge {
|
|
|
49
116
|
targetId: string;
|
|
50
117
|
/** Edge type (default: 'directed') */
|
|
51
118
|
type?: EdgeType;
|
|
119
|
+
/** Hints for layered layout algorithm */
|
|
120
|
+
layered?: LayeredEdgeHint;
|
|
121
|
+
/** Hints for force layout algorithm */
|
|
122
|
+
force?: ForceEdgeHint;
|
|
52
123
|
}
|
|
53
124
|
/**
|
|
54
125
|
* Configuration options for layout calculation
|
|
@@ -56,16 +127,22 @@ interface LayoutEdge {
|
|
|
56
127
|
interface LayoutOptions {
|
|
57
128
|
/** Layout algorithm to use (default: 'layered') */
|
|
58
129
|
algorithm?: LayoutAlgorithm;
|
|
59
|
-
/**
|
|
60
|
-
direction?: LayoutDirection;
|
|
61
|
-
/** Horizontal spacing between nodes in pixels (default: 50) */
|
|
130
|
+
/** Spacing between nodes in pixels (default: 50) */
|
|
62
131
|
nodeSpacing?: number;
|
|
63
|
-
/** Vertical spacing between layers in pixels (default: 50) */
|
|
64
|
-
layerSpacing?: number;
|
|
65
132
|
/** Padding around the entire graph in pixels (default: 20) */
|
|
66
133
|
padding?: number;
|
|
67
134
|
/** Whether edges should be considered for layout (default: true) */
|
|
68
135
|
considerEdges?: boolean;
|
|
136
|
+
/** Options for layered (hierarchical) layout */
|
|
137
|
+
layered?: LayeredLayoutOptions;
|
|
138
|
+
/** Options for force-directed layout */
|
|
139
|
+
force?: ForceLayoutOptions;
|
|
140
|
+
/** Options for stress layout */
|
|
141
|
+
stress?: StressLayoutOptions;
|
|
142
|
+
/** Options for radial layout */
|
|
143
|
+
radial?: RadialLayoutOptions;
|
|
144
|
+
/** Options for tree layout */
|
|
145
|
+
tree?: TreeLayoutOptions;
|
|
69
146
|
}
|
|
70
147
|
/**
|
|
71
148
|
* Result of layout calculation for a single node
|
|
@@ -104,10 +181,44 @@ interface LayoutInput {
|
|
|
104
181
|
/** Layout options */
|
|
105
182
|
options?: LayoutOptions;
|
|
106
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Default layered layout options
|
|
186
|
+
*/
|
|
187
|
+
declare const DEFAULT_LAYERED_OPTIONS: Required<LayeredLayoutOptions>;
|
|
188
|
+
/**
|
|
189
|
+
* Default force layout options
|
|
190
|
+
*/
|
|
191
|
+
declare const DEFAULT_FORCE_OPTIONS: Required<ForceLayoutOptions>;
|
|
192
|
+
/**
|
|
193
|
+
* Default stress layout options
|
|
194
|
+
*/
|
|
195
|
+
declare const DEFAULT_STRESS_OPTIONS: Required<StressLayoutOptions>;
|
|
196
|
+
/**
|
|
197
|
+
* Default radial layout options
|
|
198
|
+
*/
|
|
199
|
+
declare const DEFAULT_RADIAL_OPTIONS: Omit<Required<RadialLayoutOptions>, 'centerNodeId'> & {
|
|
200
|
+
centerNodeId?: string;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Default tree layout options
|
|
204
|
+
*/
|
|
205
|
+
declare const DEFAULT_TREE_OPTIONS: Required<TreeLayoutOptions>;
|
|
107
206
|
/**
|
|
108
207
|
* Default layout options
|
|
109
208
|
*/
|
|
110
|
-
declare const DEFAULT_LAYOUT_OPTIONS:
|
|
209
|
+
declare const DEFAULT_LAYOUT_OPTIONS: {
|
|
210
|
+
algorithm: LayoutAlgorithm;
|
|
211
|
+
nodeSpacing: number;
|
|
212
|
+
padding: number;
|
|
213
|
+
considerEdges: boolean;
|
|
214
|
+
layered: Required<LayeredLayoutOptions>;
|
|
215
|
+
force: Required<ForceLayoutOptions>;
|
|
216
|
+
stress: Required<StressLayoutOptions>;
|
|
217
|
+
radial: Omit<Required<RadialLayoutOptions>, "centerNodeId"> & {
|
|
218
|
+
centerNodeId?: string;
|
|
219
|
+
};
|
|
220
|
+
tree: Required<TreeLayoutOptions>;
|
|
221
|
+
};
|
|
111
222
|
|
|
112
223
|
/**
|
|
113
224
|
* Abstract layout engine interface
|
|
@@ -301,5 +412,5 @@ declare class LayoutService implements OnDestroy {
|
|
|
301
412
|
static ɵprov: i0.ɵɵInjectableDeclaration<LayoutService>;
|
|
302
413
|
}
|
|
303
414
|
|
|
304
|
-
export { DEFAULT_LAYOUT_OPTIONS, ElkLayoutEngine, LAYOUT_ENGINE, LayoutService, defaultLayoutEngineFactory };
|
|
305
|
-
export type { EdgeType, LayoutAlgorithm, LayoutDirection, LayoutEdge, LayoutEngine, LayoutInput, LayoutNode, LayoutNodeResult, LayoutOptions, LayoutResult };
|
|
415
|
+
export { DEFAULT_FORCE_OPTIONS, DEFAULT_LAYERED_OPTIONS, DEFAULT_LAYOUT_OPTIONS, DEFAULT_RADIAL_OPTIONS, DEFAULT_STRESS_OPTIONS, DEFAULT_TREE_OPTIONS, ElkLayoutEngine, LAYOUT_ENGINE, LayoutService, defaultLayoutEngineFactory };
|
|
416
|
+
export type { EdgeType, ForceEdgeHint, ForceLayoutOptions, LayeredEdgeHint, LayeredEdgeType, LayeredLayoutOptions, LayoutAlgorithm, LayoutDirection, LayoutEdge, LayoutEngine, LayoutInput, LayoutNode, LayoutNodeResult, LayoutOptions, LayoutResult, RadialLayoutOptions, StressLayoutOptions, TreeLayoutOptions };
|