# Classes

Import these from `@grafloria/renderer`.

## Classes

### `ArrowRenderer`

ArrowRenderer generates SVG VNodes for different arrow types.

Supports:
- Basic arrows (arrow, circle, square, diamond)
- ERD arrows (crow-foot, one, zero-or-one, zero-or-many, one-or-many)
- UML arrows (hollow-diamond, filled-diamond, generalization, open-arrow, double-arrow)
- Additional arrows (cross, bar, dot, oval)
- Half-arrowheads (Mermaid 11.13) — `half-arrow-left` / `half-arrow-right`
- AUTHOR-DEFINED markers: a raw SVG `path`, or anything
  registered with `registerMarker` — the catalogue is no longer a closed enum.

```ts
class ArrowRenderer
```

**Methods**

- `getTipOffset(style: ArrowStyle): number` — Distance from the marker's local origin to its forward-most point (its visual tip), in the +x direction the marker is rotated toward.
- `renderArrow( style: ArrowStyle, transform: string, backgroundColor: string = 'white', end: 'source' | 'target' = 'target' ): VNode | null` — Render an arrow based on the provided style and transform

**Example**

```typescript
const renderer = new ArrowRenderer();
const arrowVNode = renderer.renderArrow({
  type: 'crow-foot',
  size: 10,
  filled: false,
  color: '#000'
}, 'translate(100, 50) rotate(45)');
```

**Example**

 A custom marker
```typescript
registerMarker('feather', {
  tipOffset: style => style.size,
  render: ctx => ({ type: 'path', props: { d: `M0,0 L${ctx.size},0`, stroke: ctx.color, transform: ctx.transform } }),
});
link.updateStyle({ arrowHead: { type: 'feather', size: 12, filled: true } });
```

### `EdgeOptimizer`

```ts
class EdgeOptimizer
```

**Methods**

- `constructor(options: EdgeOptimizerOptions = {})`
- `get stats(): Readonly<OptimizerStats>`
- `update(frame: OptimizerFrame): void` — Run the pass for a frame. Cheap when little changed: a frame whose links and nodes all carry the same signatures as last time performs zero segment tests and zero label searches.
- `getJumps(linkId: string): Intersection[]` — The crossings on this link, for the jump-point path builder. Empty when the link draws no jumps.
- `getLabelOffset(linkId: string, labelId: string, fallback: Point): Point` — The offset this label should actually be drawn at. For a label that did not opt into `autoOffset` this is exactly the offset the author set.
- `reset(): void` — Drop all state (renderer disposal, or a wholesale diagram swap).

### `JumpPointDetector`

JumpPointDetector detects line-line intersections for jump point rendering.

Features:
- Line segment intersection detection
- Angle calculation between intersecting lines
- Multiple detection modes (all, perpendicular, threshold)
- Performance optimized for many links

Algorithm:
Uses parametric line intersection algorithm with bounds checking.

```ts
class JumpPointDetector
```

**Methods**

- `findIntersection(line1: LineSegment, line2: LineSegment): Intersection | null` — Find intersection between two line segments
- `detectIntersections( targetLink: LinkWithPoints, otherLinks: LinkWithPoints[], mode: DetectionMode = 'all', threshold: number = 45 ): Intersection[]` — Detect all intersections for a link with other links

### `JumpPointRenderer`

JumpPointRenderer modifies link paths to show jump points at intersections.

Supports three visual styles:
- arc: Small arc over intersection
- gap: Break in line
- bridge: Bridge shape over intersection

Algorithm:
1. Parse path to extract points
2. Sort intersections by position
3. Split path at intersections
4. Insert jump point geometry
5. Reconstruct path

```ts
class JumpPointRenderer
```

**Methods**

- `renderWithJumpPoints( pathData: string, intersections: Intersection[], config: JumpPointConfig, originalProps?: Record<string, any> ): VNode` — Render path with jump points at intersections

### `LabelRenderer`

LabelRenderer generates SVG (or HTML) VNodes for link labels.

Features:
- Position labels at any point along path (0-1), or in one of three slots
- Auto-rotation with path or fixed angle
- Text wrapping with maxWidth
- Multiple labels per link
- Rich styling support
- HTML content and author templates

Architecture:
Uses LinkModel utilities (getPointAtPosition, getAngleAt) for positioning. Returns VNode tree compatible with framework-agnostic rendering.

```ts
class LabelRenderer
```

**Methods**

- `renderLabel(label: LinkLabel, link: LinkModel, context: LabelRenderContext = {}): VNode | null` — Render a label on a link
- `labelBox(label: LinkLabel): { width: number; height: number }` — Estimated on-screen box of a label, in LOCAL units (before the path anchor and offset are applied). The edge optimizer needs a box to test collisions against; HTML/template labels declare their own, and text labels are measured with the same estimator the background rect already uses.

### `RouteMemo`

```ts
class RouteMemo
```

**Methods**

- `get stats(): Readonly<RouteMemoStats>`
- `get size(): number`
- `beginFrame(rects: Map<string, Rect>, obstacleEpoch: string): Rect[]` — Start a frame. Diffs this frame's node rectangles against last frame's and returns the world regions that changed — each moved node contributes BOTH where it was and where it now is, because a link routed around its old position is just as stale as one routed through its new one.
- `lookup(linkId: string, key: string): RoutedPath | undefined` — The route for this link if its inputs are unchanged since we cached it.
- `store(linkId: string, key: string, routed: RoutedPath): void`
- `drop(linkId: string): void` — A route computed but NOT cacheable (e.g. no route found) must not keep a stale entry.
- `invalidate(linkIds: Iterable<string>): void`
- `clear(): void`

### `RouteSolverBridge`

```ts
class RouteSolverBridge
```

**Methods**

- `constructor(options: RouteSolverBridgeOptions = {})`
- `get stats(): Readonly<RouteSolverStats>`
- `hasRoutesFor(version: number): boolean` — True once the solver has an answer for THIS world — i.e. one safe to paint.
- `routeFor(linkId: string, version: number): RoutedPath | undefined`
- `submit(version: number, edges: SolverEdge[], obstacles: Obstacle[]): void` — Ask for this world to be solved. Idempotent per version: submitting the same world twice does nothing, so calling this every frame is free.
- `dispose(): void`
