# Classes

Import these from `@grafloria/engine`.

## Classes

### `BaseLayoutAlgorithm`

Base abstract class for layout algorithms

Provides common functionality that all layout algorithms can use. Extend this class when implementing a new layout algorithm.

```ts
abstract class BaseLayoutAlgorithm implements ILayoutAlgorithm
```

**Methods**

- `constructor(config?: LayoutConfiguration)`
- `abstract getName(): string` — Get the name of the layout algorithm
- `abstract getType(): 'grid' | 'force-directed' | 'hierarchical' | 'hybrid'` — Get the type of the layout algorithm
- `abstract calculatePlacement(options: PlacementOptions): PlacementResult` — Calculate position for a single new node

This is called when a node is added to the diagram. The algorithm should return a position that:
- Doesn't overlap with existing nodes
- Follows the layout strategy
- Fits within the viewport (or is close to existing content)
- `abstract reLayout(diagram: DiagramModel, config?: LayoutConfiguration): Map<string, Point>` — Re-layout all nodes in the diagram

This is called when the user explicitly requests a re-layout (e.g., clicks "Re-Arrange" button). The algorithm should calculate new positions for ALL nodes.
- `configure(config: LayoutConfiguration): void` — Configure the layout algorithm
- `getConfiguration(): LayoutConfiguration` — Get current configuration
- `canApply(diagram: DiagramModel): { valid: boolean; reason?: string }` — Validate if this algorithm can be applied to the given diagram

For example:
- Hierarchical layout requires a DAG (no cycles)
- Force-directed works better with connected nodes

### `CommunityLayoutAdapter`

Community Detection Layout Adapter

Uses Louvain algorithm to detect communities and arranges them.

```ts
class CommunityLayoutAdapter implements LayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` |  | `'community'` | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Methods**

- `async apply( nodes: NodeModel[], links: LinkModel[], options: Partial<CommunityLayoutOptions> = {} ): Promise<LayoutResult>` — Apply community detection layout
- `async applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: any, layoutOptions?: Partial<CommunityLayoutOptions> ): Promise<LayoutResult & { incremental: any }>` — Apply incremental layout
- `validateOptions(options: Partial<CommunityLayoutOptions>): boolean` — Validate options

### `CompoundLayoutService`

```ts
class CompoundLayoutService
```

**Methods**

- `constructor( private readonly diagram: DiagramModel, private readonly options: CompoundLayoutOptions = {} )`
- `async layout(): Promise<CompoundLayoutResult>` — Run the compound layout over the whole diagram.

### `ConstraintManager`

Helper class for managing and applying layout constraints

```ts
class ConstraintManager
```

**Methods**

- `constructor(constraints?: LayoutConstraints)`
- `addConstraints(constraints: LayoutConstraints): void` — Add constraints to the manager
- `getConstraints(nodeId: string): NodeConstraint[]` — Get all constraints for a specific node
- `hasConstraints(nodeId: string): boolean` — Check if a node has any constraints
- `applyConstraints( nodeId: string, proposedPosition: Position, conflictResolution: 'priority' | 'first' | 'last' = 'priority' ): Position` — Apply constraints to a position, returning the constrained position
- `clear(): void` — Clear all constraints
- `removeConstraints(nodeId: string): void` — Remove constraints for a specific node
- `getConstrainedNodeCount(): number` — Get total number of constrained nodes
- `getConstrainedNodeIds(): string[]` — Get all constrained node IDs

### `DagreLayoutAdapter`

Dagre Layout Adapter

Provides hierarchical layout using the Dagre library. Converts between Grafloria's node/link model and Dagre's graph structure.

```ts
class DagreLayoutAdapter implements LayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` |  | `'dagre'` | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Methods**

- `async apply( nodes: NodeModel[], links: LinkModel[], options: Partial<DagreLayoutOptions> = {} ): Promise<LayoutResult>` — Apply Dagre layout to nodes and links
- `validateOptions(options: Partial<DagreLayoutOptions>): boolean` — Validate Dagre layout options
- `async applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: IncrementalLayoutOptions, layoutOptions?: Partial<DagreLayoutOptions> ): Promise<LayoutResult & { incremental: IncrementalLayoutResult }>` — Apply incremental layout - layout new nodes while preserving existing positions

### `EdgeBundlingManager`

Edge bundling computation utilities

```ts
class EdgeBundlingManager
```

**Methods**

- `static computeBundling( edges: EdgeInfo[], nodePositions: Map<string, Point2D>, portPositions: Map<string, Point2D>, options: EdgeBundlingOptions ): EdgeBundlingResult` (static) — Compute edge bundling for a set of edges

### `ELKLayoutAdapter`

ELK Layout Adapter

Provides advanced layout algorithms using the Eclipse Layout Kernel. Supports multiple algorithms with extensive configuration options.

```ts
class ELKLayoutAdapter implements LayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` |  | `'elk'` | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Methods**

- `async apply( nodes: NodeModel[], links: LinkModel[], options: Partial<ELKLayoutOptions> = {} ): Promise<LayoutResult>` — Apply ELK layout to nodes and links
- `validateOptions(options: Partial<ELKLayoutOptions>): boolean` — Validate ELK layout options
- `async applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: IncrementalLayoutOptions, layoutOptions?: Partial<ELKLayoutOptions> ): Promise<LayoutResult & { incremental: IncrementalLayoutResult }>` — Apply incremental layout - layout new nodes while preserving existing positions

### `ForceLayoutAdapter`

Force-Directed Layout Adapter

Implements Fruchterman-Reingold algorithm with Barnes-Hut optimization.

```ts
class ForceLayoutAdapter implements SteppableLayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` |  | `'force'` | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Methods**

- `createRun( nodes: NodeModel[], links: LinkModel[], options: Partial<ForceLayoutOptions> = {} ): LayoutRun` — The simulation, exposed one iteration at a time.

This is where the physics lives, and it is the ONLY place it lives: `apply()`
below is now just "drive this to convergence". Splitting the loop out (rather
than copying it into a worker-flavoured twin) is what keeps the off-thread
path honest — the worker and the main thread run the same arithmetic in the
same order, so they cannot drift.

Pure and synchronous: no clock, no DOM, and randomness only through the
seeded generator. Those three abstinences are the whole reason a worker run
and an inline run produce byte-identical coordinates.

`snapshot()` is meaningful after ANY number of steps, which is what makes a
cancelled force layout return the 200-iteration picture it already has
instead of throwing it away.
- `async apply( nodes: NodeModel[], links: LinkModel[], options: Partial<ForceLayoutOptions> = {} ): Promise<LayoutResult>` — Apply force-directed layout (run the simulation to convergence).
- `async applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: any, layoutOptions?: Partial<ForceLayoutOptions> ): Promise<LayoutResult & { incremental: any }>` — Apply incremental layout
- `validateOptions(options: Partial<ForceLayoutOptions>): boolean` — Validate options

### `IncrementalLayoutManager`

Helper class for managing incremental layouts

```ts
class IncrementalLayoutManager
```

**Methods**

- `static identifyNewNodes( nodes: NodeModel[], options: IncrementalLayoutOptions ): string[]` (static) — Identify new nodes that need to be laid out
- `static identifyExistingNodes( nodes: NodeModel[], newNodeIds: string[] ): string[]` (static) — Identify existing nodes that should be constrained
- `static generateConstraints( nodes: NodeModel[], options: IncrementalLayoutOptions ): LayoutConstraints` (static) — Generate constraints for incremental layout based on strategy
- `static calculateResult( nodes: NodeModel[], oldPositions: Map<string, { x: number; y: number }>, newNodeIds: string[], constraints: LayoutConstraints, strategy: IncrementalLayoutStrategy ): IncrementalLayoutResult` (static) — Calculate movement statistics after incremental layout

### `LayoutHistory`

Layout History Manager

Manages undo/redo stack for layout operations

```ts
class LayoutHistory
```

**Methods**

- `constructor(options: LayoutHistoryOptions = {})`
- `pushSnapshot( nodes: NodeModel[], description?: string, algorithm?: string, options?: any ): LayoutSnapshot` — Create and push a new snapshot
- `undo(): LayoutSnapshot | undefined` — Undo to previous snapshot
- `redo(): LayoutSnapshot | undefined` — Redo to next snapshot
- `canUndo(): boolean` — Check if undo is possible
- `canRedo(): boolean` — Check if redo is possible
- `getCurrentSnapshot(): LayoutSnapshot | undefined` — Get current snapshot
- `getAllSnapshots(): LayoutSnapshot[]` — Get all snapshots
- `getSnapshotById(id: string): LayoutSnapshot | undefined` — Get snapshot by ID
- `restoreSnapshot(id: string): LayoutSnapshot | undefined` — Restore a specific snapshot by ID
- `clear(): void` — Clear all history
- `size(): number` — Get history size
- `getCurrentIndex(): number` — Get current index in history
- `static applySnapshot(snapshot: LayoutSnapshot, nodes: NodeModel[]): number` (static) — Apply snapshot to nodes
- `exportToJSON(): string` — Export history to JSON
- `importFromJSON(json: string): boolean` — Import history from JSON
- `getStatistics(): { totalSnapshots: number; currentIndex: number; canUndo: boolean; canRedo: boolean; oldestTimestamp: number; newestTimestamp: number; averageInterval: number; }` — Get history statistics

### `LayoutHost`

The caller-side host.

Pass a real Worker (or anything satisfying `LayoutPort`) to run off-thread;
pass nothing to run inline on the same thread. Identical behaviour, no protocol
drift, because BOTH paths speak through `serveLayout`.

```ts
class LayoutHost
```

**Methods**

- `constructor(port?: LayoutPort, deps: ServeLayoutDeps = {})`
- `run( algorithm: string, graph: LayoutGraph, options: LayoutWireOptions = {}, runOptions: LayoutRunOptions = {} ): Promise<HostLayoutResult>`

### `LayoutPresets`

Predefined layout presets for common scenarios

```ts
class LayoutPresets
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `HIERARCHICAL` (static) | `LayoutPresetCategory` |  | Organizational/Hierarchical Layouts |
| `FLOW` (static) | `LayoutPresetCategory` |  | Process/Flow Layouts |
| `NETWORK` (static) | `LayoutPresetCategory` |  | Network/Graph Layouts |
| `ARCHITECTURE` (static) | `LayoutPresetCategory` |  | Architecture/System Layouts |
| `INTERACTIVE` (static) | `LayoutPresetCategory` |  | Interactive/Dashboard Layouts |

**Methods**

- `static getAllCategories(): LayoutPresetCategory[]` (static) — Get all preset categories
- `static getAllPresets(): LayoutPreset[]` (static) — Get all presets across all categories
- `static findPreset(id: string): LayoutPreset | undefined` (static) — Find preset by ID
- `static findPresetsByTag(tag: string): LayoutPreset[]` (static) — Find presets by tag
- `static findPresetsByAdapter(adapter: 'dagre' | 'elk'): LayoutPreset[]` (static) — Find presets by adapter type
- `static searchPresets(query: string): LayoutPreset[]` (static) — Search presets by name or description

### `LayoutQualityMetrics`

Layout Quality Metrics Calculator

```ts
class LayoutQualityMetrics
```

**Methods**

- `static assess( nodes: NodeModel[], links: LinkModel[], options: QualityAssessmentOptions = {} ): LayoutQualityResult` (static) — Assess the quality of a layout

### `LayoutRegistry`

The named-algorithm registry.

```ts
class LayoutRegistry
```

**Methods**

- `register(engine: RegisteredLayout): () => void`
- `get(name: string): RegisteredLayout | undefined`
- `has(name: string): boolean`
- `names(): string[]` — Registered names, sorted — a stable list is part of being deterministic.
- `adapters(): Record<string, LayoutAdapter>` — Name → adapter, for every registered engine that exposes one.

This is what nested (compound) layout resolves a container's algorithm
against — so `group.subgraphLayout = { algorithm: 'force' }` works, and so
does an algorithm an extension registered, rather than only the hard-coded
dagre|elk pair.

### `LayoutService`

Layout Service

Manages layout adapters and provides a unified API for applying layouts. Can be used as a singleton or instantiated per diagram.

```ts
class LayoutService
```

**Methods**

- `constructor()`
- `registerAdapter(adapter: LayoutAdapter): void` — Register a custom layout adapter
- `getAdapter(name: string): LayoutAdapter | undefined` — Get a registered adapter by name
- `getAdapterNames(): string[]` — Get all registered adapter names
- `async applyLayout(diagram: DiagramModel, config: ApplyLayoutConfig): Promise<LayoutResult>` — Apply layout to diagram

### `SpectralLayoutAdapter`

Spectral Layout Adapter

Uses eigendecomposition of graph Laplacian for optimal layout.

```ts
class SpectralLayoutAdapter implements LayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` |  | `'spectral'` | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Methods**

- `async apply( nodes: NodeModel[], links: LinkModel[], options: Partial<SpectralLayoutOptions> = {} ): Promise<LayoutResult>` — Apply spectral layout
- `async applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: any, layoutOptions?: Partial<SpectralLayoutOptions> ): Promise<LayoutResult & { incremental: any }>` — Apply incremental layout
- `validateOptions(options: Partial<SpectralLayoutOptions>): boolean` — Validate options
