# Layout — Sugiyama

Import these from `@grafloria/engine`.

## Functions

### `createLayeredLayout`

```ts
function createLayeredLayout(name = 'layered'): RegisteredLayout
```

### `inferDirection`

DIRECTION INFERENCE — "TB for trees, LR for pipelines".

The heuristic, stated so it can be argued with: a graph that is deep and narrow
reads better across the page (a pipeline: A → B → C → D as a row, not a column),
and a graph that is wide and shallow reads better down it (a tree fans out). The threshold is the aspect of the LAYER structure, not of the node count.

```ts
function inferDirection(nodes: SugiyamaNode[], edges: SugiyamaEdge[]): LayoutDirection
```

### `sugiyama`

Lay out a graph in layers. Pure, deterministic, no DOM, no time, no randomness.

```ts
function sugiyama(
  nodes: SugiyamaNode[],
  edges: SugiyamaEdge[],
  options: SugiyamaOptions = {}
): SugiyamaResult
```

## Interfaces

### `LayeredLayoutOptions`

Also has every member of `UnifiedLayoutOptions`, `LayoutOptions`, `LayoutRunOptions`, listed on their own entries.

```ts
interface LayeredLayoutOptions extends UnifiedLayoutOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `semantic?` | `SemanticConstraints` |  | . Honoured DURING ranking/ordering — not clamped afterwards. |
| `iterations?` | `number` |  | Ordering/coordinate sweeps. |

### `SemanticConstraints`

's semantic constraints — honoured DURING the pipeline, not clamped after. Every one of these is a decision taken in ranking or ordering.

```ts
interface SemanticConstraints
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `sameRank?` | `string[][]` |  | Groups of node ids that must share a rank/layer. Applied by contraction before ranking. |
| `order?` | `Array<[string, string]>` |  | `[a, b]` means a must come before b within its layer (left-of in TB, above in LR). |
| `keepTogether?` | `string[][]` |  | Node ids that must stay adjacent in the ordering — a cluster the sweeps may not split. |
| `anchors?` | `Record<string, { x?: number; y?: number }>` |  | Nodes pinned to a coordinate. Unlike the old clamp, everything else routes AROUND them. |

### `SugiyamaEdge`

```ts
interface SugiyamaEdge
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `source` | `string` |  |  |
| `target` | `string` |  |  |

### `SugiyamaNode`

```ts
interface SugiyamaNode
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `width` | `number` |  |  |
| `height` | `number` |  |  |

### `SugiyamaOptions`

```ts
interface SugiyamaOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `direction?` | `LayoutDirection` |  |  |
| `nodeSpacing?` | `number` |  | Gap between nodes in the same layer. |
| `rankSpacing?` | `number` |  | Gap between layers. |
| `constraints?` | `SemanticConstraints` |  |  |
| `iterations?` | `number` |  | Ordering sweeps. More = fewer crossings, diminishing fast. |
| `rng?` | `LayoutRng` |  |  |

### `SugiyamaResult`

```ts
interface SugiyamaResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `positions` | `Map<string, { x: number; y: number }>` |  |  |
| `ranks` | `Map<string, number>` |  |  |
| `bends` | `Map<string, Array<{ x: number; y: number }>>` |  | Bend points for edges that span more than one rank (the dummy chains). |
| `crossings` | `number` |  | Crossings in the final ordering — the headline quality number. |
| `stats` | `SugiyamaStats` |  | Deterministic work counters — regression tests bound these instead of wall clock. |

### `SugiyamaStats`

Work counters, filled on every run. They exist because "it got slow" is only
fixable when you can see WHERE the work went: a wide layer full of dummies made
the old transpose recount every inter-layer crossing twice per candidate swap
(O(width * E²) per pass), which is the difference between 40ms and being killed
at 25s on the same node count. The counters are pure functions of the input —
no wall clock — so CI can assert hard bounds without flaking.

```ts
interface SugiyamaStats
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `dummyCount` | `number` |  |  |
| `layerCount` | `number` |  |  |
| `maxLayerWidth` | `number` |  |  |
| `transposePasses` | `number` |  | transpose() while-passes actually run (bounded by guard × ordering iterations). |
| `transposeSwapsEvaluated` | `number` |  | adjacent pairs considered for swapping, across all passes |
| `transposeSwapsApplied` | `number` |  | swaps that were kept because they reduced crossings |
| `crossingCountOps` | `number` |  | elements pushed through inversion counting in countCrossings, across the run |

## Types

### `LayoutDirection`

Also has every member of `String`, listed on its own entry.

```ts
type LayoutDirection = 'TB' | 'BT' | 'LR' | 'RL';
```
