@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.
@@ -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 = { ...DEFAULT_LAYOUT_OPTIONS, ...input.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 edges = options.considerEdges
94
- ? this.buildElkEdges(input.edges)
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
- // Add the primary edge (source → target)
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[options.algorithm],
132
- 'elk.spacing.nodeNode': String(options.nodeSpacing),
133
- 'elk.padding': `[top=${options.padding}, left=${options.padding}, bottom=${options.padding}, right=${options.padding}]`,
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 (options.algorithm) {
137
- case 'layered':
138
- elkOptions['elk.direction'] = ELK_DIRECTIONS[options.direction];
139
- elkOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = String(options.layerSpacing);
140
- // Improve edge routing
141
- elkOptions['elk.layered.spacing.edgeNodeBetweenLayers'] = String(options.layerSpacing / 2);
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
- case 'tree':
144
- elkOptions['elk.direction'] = ELK_DIRECTIONS[options.direction];
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
- case 'force':
148
- // Force-directed doesn't use direction
149
- elkOptions['elk.force.iterations'] = '300';
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
- case 'stress':
152
- // Stress-based layout options
153
- elkOptions['elk.stress.desiredEdgeLength'] = String(options.nodeSpacing * 2);
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
- case 'radial':
156
- // Radial layout options
157
- elkOptions['elk.radial.radius'] = String(options.layerSpacing);
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":";;;;AAAA;;;;;AAKG;AA0NH;;AAEG;AACI,MAAM,uBAAuB,GAAmC;AACrE,IAAA,SAAS,EAAE,MAAM;AACjB,IAAA,YAAY,EAAE,EAAE;;AAGlB;;AAEG;AACI,MAAM,qBAAqB,GAAiC;AACjE,IAAA,UAAU,EAAE,GAAG;AACf,IAAA,iBAAiB,EAAE,CAAC;;AAGtB;;AAEG;AACI,MAAM,sBAAsB,GAAkC;AACnE,IAAA,UAAU,EAAE,GAAG;AACf,IAAA,gBAAgB,EAAE,CAAC;;AAGrB;;AAEG;AACI,MAAM,sBAAsB,GAAoF;AACrH,IAAA,YAAY,EAAE,SAAS;AACvB,IAAA,eAAe,EAAE,GAAG;;AAGtB;;AAEG;AACI,MAAM,oBAAoB,GAAgC;AAC/D,IAAA,SAAS,EAAE,MAAM;;AAGnB;;AAEG;AACI,MAAM,sBAAsB,GAAG;AACpC,IAAA,SAAS,EAAE,SAA4B;AACvC,IAAA,WAAW,EAAE,EAAE;AACf,IAAA,OAAO,EAAE,EAAE;AACX,IAAA,aAAa,EAAE,IAAI;AACnB,IAAA,OAAO,EAAE,uBAAuB;AAChC,IAAA,KAAK,EAAE,qBAAqB;AAC5B,IAAA,MAAM,EAAE,sBAAsB;AAC9B,IAAA,MAAM,EAAE,sBAAsB;AAC9B,IAAA,IAAI,EAAE,oBAAoB;;;AChQ5B;;;AAGG;AACH,MAAM,cAAc,GAAoC;AACtD,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,IAAI,EAAE,QAAQ;CACf;AAED;;;AAGG;AACH,MAAM,cAAc,GAAoC;AACtD,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,EAAE,EAAE,IAAI;AACR,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,IAAI,EAAE,MAAM;CACb;AAED;;;;;AAKG;MACU,eAAe,CAAA;IACjB,IAAI,GAAG,KAAK;AAEb,IAAA,GAAG;AAEX,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE;IACtB;IAEA,MAAM,eAAe,CAAC,KAAkB,EAAA;AACtC,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE;;QAGnC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC;;QAGnD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;;AAGrD,QAAA,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC;IAC3C;IAEA,OAAO,GAAA;;;IAGP;AAEA;;AAEG;IACK,aAAa,CACnB,KAAkB,EAClB,OAAsB,EAAA;QAEtB,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;;QAGhD,MAAM,QAAQ,GAAc,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;AACnD,YAAA,MAAM,OAAO,GAAY;gBACvB,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB;;AAGD,YAAA,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE;AAC1D,gBAAA,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM;AACvB,gBAAA,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM;;gBAEvB,OAAO,CAAC,aAAa,GAAG;oBACtB,cAAc,EAAE,IAAI,IAAI,CAAC,MAAM,CAAA,EAAA,EAAK,IAAI,CAAC,MAAM,CAAA,CAAA,CAAG;iBACnD;YACH;AAEA,YAAA,OAAO,OAAO;AAChB,QAAA,CAAC,CAAC;;QAGF,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC,aAAa;QACnF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,sBAAsB,CAAC,SAAS;QACvE,MAAM,KAAK,GAAsB;cAC7B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS;cACzC,EAAE;QAEN,OAAO;AACL,YAAA,EAAE,EAAE,MAAM;AACV,YAAA,aAAa,EAAE,UAAU;YACzB,QAAQ;YACR,KAAK;SACN;IACH;AAEA;;AAEG;IACK,aAAa,CAAC,KAAmB,EAAE,SAA0B,EAAA;QACnE,MAAM,QAAQ,GAAsB,EAAE;AAEtC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,YAAA,MAAM,OAAO,GAAoB;gBAC/B,EAAE,EAAE,IAAI,CAAC,EAAE;AACX,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;AACxB,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;aACzB;;YAGD,IAAI,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE;AAC3C,gBAAA,OAAO,CAAC,aAAa,GAAG,EAAE;;gBAG1B,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE;AACvC,oBAAA,OAAO,CAAC,aAAa,CAAC,gCAAgC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACzF;YACF;;AAGA,YAAA,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;;AAGtB,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,EAAE;gBACjC,QAAQ,CAAC,IAAI,CAAC;AACZ,oBAAA,EAAE,EAAE,CAAA,EAAG,IAAI,CAAC,EAAE,CAAA,QAAA,CAAU;AACxB,oBAAA,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;AACxB,oBAAA,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;oBACxB,aAAa,EAAE,OAAO,CAAC,aAAa;AACrC,iBAAA,CAAC;YACJ;QACF;AAEA,QAAA,OAAO,QAAQ;IACjB;AAEA;;AAEG;AACK,IAAA,eAAe,CAAC,OAAsB,EAAA;QAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,sBAAsB,CAAC,SAAS;QACvE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,sBAAsB,CAAC,WAAW;QAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,sBAAsB,CAAC,OAAO;AAEjE,QAAA,MAAM,UAAU,GAAqB;AACnC,YAAA,eAAe,EAAE,cAAc,CAAC,SAAS,CAAC;AAC1C,YAAA,sBAAsB,EAAE,MAAM,CAAC,WAAW,CAAC;YAC3C,aAAa,EAAE,QAAQ,OAAO,CAAA,OAAA,EAAU,OAAO,CAAA,SAAA,EAAY,OAAO,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA,CAAG;SACxF;;QAGD,QAAQ,SAAS;YACf,KAAK,SAAS,EAAE;AACd,gBAAA,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,IAAI,uBAAuB;gBAC9D,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,IAAI,uBAAuB,CAAC,SAAS;gBAC5E,MAAM,YAAY,GAAG,WAAW,CAAC,YAAY,IAAI,uBAAuB,CAAC,YAAY;gBAErF,UAAU,CAAC,eAAe,CAAC,GAAG,cAAc,CAAC,SAAS,CAAC;gBACvD,UAAU,CAAC,2CAA2C,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;gBAC9E,UAAU,CAAC,2CAA2C,CAAC,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;gBAClF;YACF;YAEA,KAAK,MAAM,EAAE;AACX,gBAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,IAAI,oBAAoB;gBACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,oBAAoB,CAAC,SAAS;gBAEtE,UAAU,CAAC,eAAe,CAAC,GAAG,cAAc,CAAC,SAAS,CAAC;AACvD,gBAAA,UAAU,CAAC,sBAAsB,CAAC,GAAG,YAAY;gBACjD;YACF;YAEA,KAAK,OAAO,EAAE;AACZ,gBAAA,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE;gBACrC,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,IAAI,qBAAqB,CAAC,UAAU;gBAC3E,MAAM,SAAS,GAAG,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,iBAAiB;gBAExF,UAAU,CAAC,sBAAsB,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;gBACvD,UAAU,CAAC,qBAAqB,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC;gBACrD;YACF;YAEA,KAAK,QAAQ,EAAE;AACb,gBAAA,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE;gBACvC,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,sBAAsB,CAAC,UAAU;gBAC7E,MAAM,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,IAAI,sBAAsB,CAAC,gBAAgB;AAE/F,gBAAA,UAAU,CAAC,8BAA8B,CAAC,GAAG,MAAM,CAAC,WAAW,GAAG,CAAC,GAAG,gBAAgB,CAAC;gBACvF,UAAU,CAAC,2BAA2B,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;gBAC5D;YACF;YAEA,KAAK,QAAQ,EAAE;AACb,gBAAA,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE;gBACvC,MAAM,eAAe,GAAG,UAAU,CAAC,eAAe,IAAI,sBAAsB,CAAC,eAAe;gBAE5F,UAAU,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;;;gBAGzD;YACF;;AAGF,QAAA,OAAO,UAAU;IACnB;AAEA;;AAEG;AACK,IAAA,cAAc,CAAC,QAAiB,EAAA;AACtC,QAAA,MAAM,KAAK,GAAuB,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,KAAK,MAAM;YAC1E,EAAE,EAAE,KAAK,CAAC,EAAE;AACZ,YAAA,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC;AACf,YAAA,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC;AACf,YAAA,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;AACvB,YAAA,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;AAC1B,SAAA,CAAC,CAAC;QAEH,OAAO;YACL,KAAK;AACL,YAAA,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC;AAC1B,YAAA,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,CAAC;SAC7B;IACH;AACD;;AC3OD;;;;;;;;;;AAUG;MACU,aAAa,GAAG,IAAI,cAAc,CAAe,eAAe;AAE7E;;AAEG;SACa,0BAA0B,GAAA;IACxC,OAAO,IAAI,eAAe,EAAE;AAC9B;AAEA;;;;;;;;;;;;;;;;;;;;;AAqBG;MAEU,aAAa,CAAA;AAChB,IAAA,MAAM;AAEd,IAAA,WAAA,GAAA;;AAEE,QAAA,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAChE,QAAA,IAAI,CAAC,MAAM,GAAG,cAAc,IAAI,0BAA0B,EAAE;IAC9D;AAEA;;AAEG;AACH,IAAA,IAAI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI;IACzB;AAEA;;;;;AAKG;IACH,MAAM,eAAe,CAAC,KAAkB,EAAA;;AAEtC,QAAA,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAEzB,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC;IAC3C;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,MAAM,CACV,KAAmB,EACnB,KAAmB,EACnB,OAAuB,EAAA;AAEvB,QAAA,OAAO,IAAI,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACxD;AAEA;;;;;;AAMG;IACH,MAAM,kBAAkB,CACtB,KAAkB,EAAA;QAElB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;AAEhD,QAAA,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoC;AAC/D,QAAA,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE;YAC/B,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QACpD;AAEA,QAAA,OAAO,WAAW;IACpB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;IACH,MAAM,iBAAiB,CACrB,KAAmB,EACnB,KAAmB,EACnB,iBAAwD,EACxD,OAAuB,EAAA;;QAGvB,MAAM,uBAAuB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;YACjD,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,IAAI,WAAW,EAAE;gBACf,OAAO;AACL,oBAAA,GAAG,IAAI;oBACP,MAAM,EAAE,WAAW,CAAC,CAAC;oBACrB,MAAM,EAAE,WAAW,CAAC,CAAC;iBACtB;YACH;AACA,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;QAEF,OAAO,IAAI,CAAC,eAAe,CAAC;AAC1B,YAAA,KAAK,EAAE,uBAAuB;YAC9B,KAAK;YACL,OAAO;AACR,SAAA,CAAC;IACJ;AAEA;;;;;;;;;;;AAWG;IACH,MAAM,gBAAgB,CACpB,cAA4B,EAC5B,QAAsB,EACtB,QAAsB,EACtB,OAAuB,EAAA;;AAGvB,QAAA,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAoC;AACrE,QAAA,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,EAAE;YACvC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1D;;AAGA,QAAA,MAAM,aAAa,GAAiB,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM;YACnE,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,MAAM,EAAE,CAAC,CAAC,MAAM;AACjB,SAAA,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,QAAQ,CAAC;AAEhD,QAAA,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,CAAC;IAC/E;AAEA;;;;;;;;;;;;AAYG;AACH,IAAA,MAAM,qBAAqB,CACzB,cAA4B,EAC5B,eAAyB,EACzB,QAAsB,EACtB,OAAuB,EACvB,iBAAiB,GAAG,IAAI,EAAA;AAExB,QAAA,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC;;AAG1C,QAAA,MAAM,cAAc,GAAiB,cAAc,CAAC;AACjD,aAAA,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,aAAA,GAAG,CAAC,CAAC,CAAC,MAAM;YACX,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,MAAM,EAAE,CAAC,CAAC,MAAM;AACjB,SAAA,CAAC,CAAC;;AAGL,QAAA,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CACpC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAChE;AAED,QAAA,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE;AAC/B,YAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;QAC3C;QAEA,IAAI,iBAAiB,EAAE;AACrB,YAAA,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAoC;AACrE,YAAA,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,EAAE;gBACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;oBAC3B,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC1D;YACF;AACA,YAAA,OAAO,IAAI,CAAC,iBAAiB,CAC3B,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,OAAO,CACR;QACH;QAEA,OAAO,IAAI,CAAC,eAAe,CAAC;AAC1B,YAAA,KAAK,EAAE,cAAc;AACrB,YAAA,KAAK,EAAE,cAAc;YACrB,OAAO;AACR,SAAA,CAAC;IACJ;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI;IACzB;AAEA;;AAEG;AACK,IAAA,aAAa,CAAC,KAAkB,EAAA;AACtC,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AAC5C,YAAA,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC;QACjE;;AAGA,QAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU;AACjC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE;YAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;gBACxB,MAAM,IAAI,KAAK,CAAC,CAAA,kCAAA,EAAqC,IAAI,CAAC,EAAE,CAAA,CAAA,CAAG,CAAC;YAClE;AACA,YAAA,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB;;AAGA,QAAA,IAAI,KAAK,CAAC,KAAK,EAAE;AACf,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE;gBAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AAC/B,oBAAA,MAAM,IAAI,KAAK,CACb,CAAA,qBAAA,EAAwB,IAAI,CAAC,EAAE,CAAA,kCAAA,EAAqC,IAAI,CAAC,QAAQ,CAAA,CAAA,CAAG,CACrF;gBACH;gBACA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AAC/B,oBAAA,MAAM,IAAI,KAAK,CACb,CAAA,qBAAA,EAAwB,IAAI,CAAC,EAAE,CAAA,kCAAA,EAAqC,IAAI,CAAC,QAAQ,CAAA,CAAA,CAAG,CACrF;gBACH;YACF;QACF;IACF;wGA1PW,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAb,aAAa,EAAA,CAAA;;4FAAb,aAAa,EAAA,UAAA,EAAA,CAAA;kBADzB;;;AC/BD;;ACvBA;;AAEG;;;;"}
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
- /** Direction for hierarchical layouts (default: 'DOWN') */
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: Required<LayoutOptions>;
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngx-km/layout",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=19.0.0",
6
6
  "@angular/core": ">=19.0.0"