# Diagram intent and rendering

A Grafloria spec records what nodes represent, where they belong, and how links relate them; the renderer computes link routes and paints the visible diagram.

## One model, two layers

The spec is the input vocabulary shared by Grafloria's framework bindings. Bindings reconcile specs into the live diagram model; the renderer reads that model to choose geometry and draw it. The [diagram instance](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-renderer-instance-diagraminstance) is the facade to the mounted canvas, its data model, and its engine.

```mermaid
flowchart LR
  S["Node and edge specs"] --> M["Live diagram model"]
  M --> R["Renderer: route and paint"]
  R --> V["Visible diagram"]
  U["User edit"] --> M
```

For nodes, the spec describes geometry such as position and size along with a label and application data. For edges, `source` and `target` identify nodes; optional handles express endpoint intent. Omit handles to let the renderer select the port-facing side as nodes move, or name a handle to pin an endpoint. A `router` chooses the route, while `connector` controls how a routed polyline is drawn. `type` is the edge shape shorthand; when router and connector are omitted, they are derived from it.

The model stores intent rather than a drawing frozen into pixels. An obstacle-avoiding route, for example, is recalculated from the current node positions. A hand-edited route is different: explicit waypoints record the bends the renderer must preserve.

## Render a routed diagram

Use [the `render` function](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-element-core#render) when you want to mount a spec directly in a browser host. The sample's nodes place a wall between two endpoints, and its edge requests an orthogonal route with obstacle avoidance.

```html
<div id="app" style="width: 800px; height: 420px"></div>
```

```ts
import { render } from '@grafloria/element';
import type { RenderSpec } from '@grafloria/element';
import type { DiagramInstance, EdgeSpec, NodeSpec } from '@grafloria/renderer';

const nodes: NodeSpec[] = [
  { id: 'start', position: { x: 80, y: 150 }, size: { width: 130, height: 56 }, label: 'Start' },
  { id: 'wall', position: { x: 350, y: 90 }, size: { width: 140, height: 170 }, label: 'Obstacle' },
  { id: 'finish', position: { x: 650, y: 150 }, size: { width: 130, height: 56 }, label: 'Finish' },
];

const edges: EdgeSpec[] = [
  { id: 'path', source: 'start', target: 'finish', type: 'orthogonal', router: 'avoid' },
];

const spec: RenderSpec = { nodes, edges };
const container = document.getElementById('app')!;
container.style.width = '800px';
container.style.setProperty('height', '420px', 'important');
const instance: DiagramInstance = render(spec, container);
instance.fitView();
```

`fitView()` frames all three nodes in the mounted canvas. `router: 'avoid'` selects obstacle avoidance, and `type: 'orthogonal'` gives the routed path right-angle geometry. The endpoint handles are omitted, so the renderer uses the port-facing attachment behavior rather than pinning either end to a named side.

## The same intent in framework bindings

The framework component changes how you mount and size the canvas; the `NodeSpec` and [`EdgeSpec`](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/grafloria-renderer-instance-edgespec#edgespec) data still describe the same graph. These examples use default data so the component owns the initial specs rather than receiving controlled state.

### Angular

```ts
import { Component } from '@angular/core';
import { DiagramCanvasComponent } from '@grafloria/angular';
import type { EdgeSpec, NodeSpec } from '@grafloria/renderer';

@Component({
  selector: 'app-diagram-intent',
  standalone: true,
  imports: [DiagramCanvasComponent],
  template: `
    <div class="canvas">
      <grafloria-diagram-canvas [(nodes)]="nodes" [(edges)]="edges" />
    </div>
  `,
  styles: [':host { display: block; } .canvas { height: 420px; }'],
})
export class DiagramIntentExampleComponent {
  nodes: NodeSpec[] = [
    { id: 'start', position: { x: 80, y: 150 }, size: { width: 130, height: 56 }, label: 'Start' },
    { id: 'wall', position: { x: 350, y: 90 }, size: { width: 140, height: 170 }, label: 'Obstacle' },
    { id: 'finish', position: { x: 650, y: 150 }, size: { width: 130, height: 56 }, label: 'Finish' },
  ];

  edges: EdgeSpec[] = [
    { id: 'path', source: 'start', target: 'finish', type: 'orthogonal', router: 'avoid' },
  ];
}
```

The Angular canvas starts from the same nodes and edge. Two-way bindings keep the component's edited node and edge arrays in the component fields.

## Keep the distinction clear

- Use node positions and sizes to describe box geometry; use edge endpoints and handles to describe connection intent.
- Choose a `router` for where a line travels and a `connector` for how its path is drawn. `type` is the shorthand shape setting, not a node position.
- Use `waypoints` when the bends themselves are part of the saved intent. Without manual waypoints, the route follows the current endpoints and routing rules.
- The spec passed to `render()` is an object (or its JSON), not a Mermaid-style text DSL. Use the text import API when your input is diagram text.

For a running view of an edge routed around a movable obstacle, see the [edge-routing demo](https://grafloria.com/demos/edges/edge-routing.html). For the broader model vocabulary, continue to [The graph model and document](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/graph-model-and-document), then see [Route and edit edges](https://bench-grafloria-6l.atloria.app/p/bench-grafloria-6l-dqcsEdUbfS/developer/route-and-edit-edges) for user-edited bends.
