npm package: @runiq/layout-base
Description: Layout engine interfaces and adapters for Runiq
Author: Runiq Team
Latest version: 1.2.0

# @runiq/layout-base

Layout engine package for Runiq diagram system. Provides automatic graph layout using **ELK (Eclipse Layout Kernel)**.

## Features

- ✅ **ELK Layout Engine** - Modern, actively maintained (replaced Dagre in Oct 2025)
- ✅ **Multiple Algorithms** - Layered (hierarchical), Force, Stress, Tree, Radial, Box
- ✅ **Superior Quality** - Better edge routing, crossing minimization
- ✅ **Manual Positioning** - Support for fixed node positions (hybrid layout)
- ✅ **Flexible Configuration** - Direction, spacing, and algorithm options
- ✅ **High Performance** - Handles 50+ nodes efficiently
- ✅ **TypeScript** - Full type safety

## Installation

```bash
pnpm add @runiq/layout-base
```

## Usage

### Basic Usage

```typescript
import { ElkLayoutEngine } from '@runiq/layout-base';
import type { DiagramAst } from '@runiq/core';

const engine = new ElkLayoutEngine();

const diagram: DiagramAst = {
  astVersion: '1.0',
  nodes: [
    { id: 'A', shape: 'rounded', label: 'Start' },
    { id: 'B', shape: 'rect', label: 'Process' },
    { id: 'C', shape: 'rounded', label: 'End' },
  ],
  edges: [
    { from: 'A', to: 'B' },
    { from: 'B', to: 'C' },
  ],
};

const result = await engine.layout(diagram);

console.log(result.nodes); // Positioned nodes with x, y, width, height
console.log(result.edges); // Routed edges with points
console.log(result.size); // Overall diagram dimensions
```

### Layout Options

```typescript
// Direction
await engine.layout(diagram, { direction: 'LR' }); // Left-to-Right
await engine.layout(diagram, { direction: 'TB' }); // Top-to-Bottom (default)
await engine.layout(diagram, { direction: 'BT' }); // Bottom-to-Top
await engine.layout(diagram, { direction: 'RL' }); // Right-to-Left

// Spacing
await engine.layout(diagram, { spacing: 50 }); // Compact
await engine.layout(diagram, { spacing: 80 }); // Default
await engine.layout(diagram, { spacing: 150 }); // Spacious

// Combined
await engine.layout(diagram, {
  direction: 'LR',
  spacing: 100,
});
```

### Result Structure

```typescript
interface LaidOutDiagram {
  nodes: PositionedNode[];
  edges: RoutedEdge[];
  size: { width: number; height: number };
}

interface PositionedNode {
  id: string;
  x: number; // Top-left corner
  y: number; // Top-left corner
  width: number;
  height: number;
}

interface RoutedEdge {
  from: string;
  to: string;
  points: { x: number; y: number }[]; // Routing waypoints
}
```

## ELK Algorithm Details

The layout engine uses ELK's **layered algorithm** by default, which is optimal for:

- ✅ Flowcharts
- ✅ Process diagrams
- ✅ State machines
- ✅ Hierarchical graphs
- ✅ Decision trees

### ELK Configuration

The engine automatically configures ELK with optimal settings:

```javascript
{
  'elk.algorithm': 'layered',
  'elk.direction': 'DOWN',
  'elk.spacing.nodeNode': '80',
  'elk.layered.spacing.nodeNodeBetweenLayers': '80',
  'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
  'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
  'elk.padding': '[top=20,left=20,bottom=20,right=20]',
}
```

## Migration from Dagre

If you were using the old Dagre-based layout engine, migration is straightforward:

### Before (Dagre)

```typescript
import { DagreLayoutEngine } from '@runiq/layout-base';

const engine = new DagreLayoutEngine();
const result = await engine.layout(diagram);
```

### After (ELK)

```typescript
import { ElkLayoutEngine } from '@runiq/layout-base';

const engine = new ElkLayoutEngine();
const result = await engine.layout(diagram);
```

**Changes:**

- ✅ Better layout quality (fewer edge crossings)
- ✅ More reliable edge routing
- ✅ Better handling of complex branching
- ✅ Support for manual positioning (future)
- ⚠️ Bundle size increased: 50KB → 300KB (acceptable tradeoff)

## Performance

Benchmarks on a modern laptop:

| Nodes | Layout Time | Notes                       |
| ----- | ----------- | --------------------------- |
| 10    | ~10ms       | Instant                     |
| 50    | ~50ms       | Fast                        |
| 100   | ~100ms      | Still responsive            |
| 500+  | ~500ms+     | May want progress indicator |

ELK is efficient for most real-world diagrams (< 100 nodes).

## Why ELK?

We migrated from Dagre to ELK in October 2025 because:

1. **Dagre is Abandoned** - No updates since 2022, multiple known bugs
2. **Mermaid Migrated Too** - Mermaid.js moved to ELK for the same reasons
3. **Better Quality** - ELK produces superior layouts with fewer edge crossings
4. **Active Maintenance** - ELK is backed by Eclipse Foundation
5. **More Features** - Multiple algorithms, better configuration options
6. **Manual Positioning** - Supports hybrid manual/auto layout (future feature)

## Available Algorithms

ELK supports multiple layout algorithms via the `algorithm` option:

- ✅ **layered** (default) - Hierarchical layout for flowcharts, processes
- ✅ **force** - Force-directed layout for networks, organic graphs
- ✅ **stress** - Stress minimization for similarity graphs
- ✅ **radial** - Radial tree layout
- ✅ **mrtree** - Multi-root tree layout

See [Force-Directed Networks Guide](../../docs/guide/force-directed-networks.md) for details.

## Future Features

Planned enhancements:

- 🔜 **Hybrid Layout** - Mix manual + automatic positioning
- 🔜 **Constraint System** - Relative positioning (e.g., "B below A")
- 🔜 **Subgraph Support** - Nested diagram containers
- 🔜 **Port Constraints** - Control edge attachment points

## API

### `ElkLayoutEngine`

```typescript
class ElkLayoutEngine implements LayoutEngine {
  id: string; // 'elk'
  supportsManualPositions: boolean; // true

  layout(diagram: DiagramAst, options?: LayoutOptions): Promise<LaidOutDiagram>;
}
```

### `LayoutOptions`

```typescript
interface LayoutOptions {
  direction?: 'TB' | 'BT' | 'LR' | 'RL'; // Layout direction
  spacing?: number; // Node spacing in pixels
}
```

## Examples

### Flowchart

```typescript
const flowchart: DiagramAst = {
  astVersion: '1.0',
  nodes: [
    { id: 'Start', shape: 'rounded' },
    { id: 'Process', shape: 'rect' },
    { id: 'Decision', shape: 'rhombus' },
    { id: 'Action1', shape: 'rect' },
    { id: 'Action2', shape: 'rect' },
    { id: 'End', shape: 'rounded' },
  ],
  edges: [
    { from: 'Start', to: 'Process' },
    { from: 'Process', to: 'Decision' },
    { from: 'Decision', to: 'Action1', label: 'Yes' },
    { from: 'Decision', to: 'Action2', label: 'No' },
    { from: 'Action1', to: 'End' },
    { from: 'Action2', to: 'End' },
  ],
};

const layout = await engine.layout(flowchart);
```

### Linear Chain

```typescript
const chain: DiagramAst = {
  astVersion: '1.0',
  direction: 'LR',
  nodes: [
    { id: 'Step1', shape: 'rect', label: 'Input' },
    { id: 'Step2', shape: 'rect', label: 'Transform' },
    { id: 'Step3', shape: 'rect', label: 'Validate' },
    { id: 'Step4', shape: 'rect', label: 'Output' },
  ],
  edges: [
    { from: 'Step1', to: 'Step2' },
    { from: 'Step2', to: 'Step3' },
    { from: 'Step3', to: 'Step4' },
  ],
};

const layout = await engine.layout(chain, { spacing: 120 });
```

### Complex Branching

```typescript
const branches: DiagramAst = {
  astVersion: '1.0',
  nodes: Array.from({ length: 20 }, (_, i) => ({
    id: `Node${i}`,
    shape: 'rect',
  })),
  edges: [
    // Linear backbone
    ...Array.from({ length: 19 }, (_, i) => ({
      from: `Node${i}`,
      to: `Node${i + 1}`,
    })),
    // Cross-connections
    { from: 'Node0', to: 'Node5' },
    { from: 'Node5', to: 'Node10' },
    { from: 'Node10', to: 'Node15' },
  ],
};

const layout = await engine.layout(branches);
```

## Testing

Run tests:

```bash
pnpm test
```

The package includes comprehensive tests:

- ✅ Basic layouts (single, chain, branching)
- ✅ Direction options (TB, LR, BT, RL)
- ✅ Spacing configuration
- ✅ Edge routing
- ✅ Node labels
- ✅ Different shape sizes
- ✅ Error handling
- ✅ Large diagrams (20-50 nodes)
- ✅ Deterministic output
- ✅ Edge crossing minimization

**24 tests, all passing**

## Resources

- **ELK Documentation**: https://www.eclipse.org/elk/
- **ELK.js (JavaScript wrapper)**: https://github.com/kieler/elkjs
- **ELK Algorithms**: https://www.eclipse.org/elk/reference.html
- **Runiq Core Types**: `@runiq/core` package

## License

MIT

## Contributing

Issues and PRs welcome! Please include tests for new features.

## Changelog

### v0.1.0 (October 2025)

- ✅ Migrated from Dagre to ELK
- ✅ Implemented layered algorithm
- ✅ 24 comprehensive tests
- ✅ Direction and spacing options
- ✅ Superior layout quality
- ✅ Faster performance on large diagrams