# Plot 2D

Draws a 2D scene with plain SVG: lines, points, vectors and text, with an optional play bar. It does no math and has no dependencies; it only draws the data it is given.

A scene is a plain object: `bounds` (the visible math window), `duration` in seconds (0 for a static plot) and a pure `frame(t)` function that returns the items to draw. Hand-write one, or take it from `@indoku/science` (for example `projectileScene()` or `waveScene()`); the shape is the same.

Group: Science

## Import

```tsx
import { MathRenderer, Plot2D } from "@indoku/ui"
import type { Scene2DLike } from "@indoku/ui"

const renderLatex = (latex: string) => <MathRenderer latex={latex} />
```

## Anatomy

```text
<Plot2D scene={{ kind: "2d", bounds, duration, loop?, latex?, frame(t) }} />

frame(t) returns Item2D[]:
  { t: "line",   pts: [x, y][], color?, width?, dash? }
  { t: "point",  x, y, r?, color?, label? }
  { t: "vector", x, y, dx, dy, color?, label? }
  { t: "text",   x, y, text, color? }

colors: "accent" | "muted" | "ok" | "warn" | "danger" | any CSS color
```

## Examples

### Animated scene

An animated scene plays on its own and shows a play bar with a scrubber and speed buttons. Animation starts paused when the user prefers reduced motion.

```tsx
function Plot2DBasic() {
  return <Plot2D scene={wave} />
}
```

### Controlled time

Pass `t` to drive the plot from your own state (a slider, a lesson step, scroll position). The play bar is hidden.

```tsx
function Plot2DControlled() {
  const [t, setT] = useState(0.5)
  return (
    <div>
      <input type="range" min={0} max={2} step={0.01} value={t} onChange={(e) => setT(parseFloat(e.target.value))} style={{ width: "100%", marginBottom: 8 }} />
      <Plot2D scene={wave} t={t} />
    </div>
  )
}
```

### Vectors, dashes and formulas

Vectors point in data coordinates. `renderLatex` receives each string in `scene.latex`. Pass a `renderLatex` built on `MathRenderer` to typeset them with KaTeX.

```tsx
function Plot2DVectors() {
  return <Plot2D scene={projectile} aspect={2.2} renderLatex={renderLatex} />
}
```

### Static scene

`duration: 0` makes a static plot with no play bar. Turn the grid off with `grid={false}`.

```tsx
function Plot2DStatic() {
  const parabola: Scene2DLike = {
    kind: "2d",
    bounds: { xMin: -1, xMax: 4, yMin: -1, yMax: 3 },
    duration: 0,
    frame: () => [
      { t: "line", pts: Array.from({ length: 101 }, (_, i) => [-1 + i * 0.05, (-1 + i * 0.05) ** 2 - 3 * (-1 + i * 0.05) + 2] as [number, number]), color: "accent", width: 2 },
      { t: "point", x: 1, y: 0, color: "ok", label: "x₁" },
      { t: "point", x: 2, y: 0, color: "ok", label: "x₂" },
      { t: "point", x: 1.5, y: -0.25, color: "warn", label: "vertex" },
    ],
  }
  return <Plot2D scene={parabola} grid={false} />
}
```

## Props

### Plot2D

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `scene` * | `Scene2DLike` |  | The scene to draw: bounds, duration and a pure frame(t) function. |
| `t` | `number` |  | Controlled time in seconds. Omit to let the component animate itself. |
| `controls` | `boolean` | `true` | Show the play bar for animated scenes (only when `t` is not set). |
| `aspect` | `number` | `16 / 9` | Width divided by height of the plot area. |
| `grid` | `boolean` | `true` | Draw the background grid. |
| `renderLatex` | `(latex: string) => ReactNode` |  | Renders each string of `scene.latex` below the plot. Use `(latex) => <MathRenderer latex={latex} />`, or your own renderer. Omit to hide them. |

`*` = required. Style props and HTML attributes are accepted too.

## Accessibility

- The SVG has `role="img"` and uses `scene.title` as its label.
- The play bar buttons and the time slider are real form controls with labels, so they work with the keyboard.
- Autoplay is off when `prefers-reduced-motion` is set; the user can still press Play or drag the scrubber.

## Notes

- Keep `frame(t)` pure and cheap: it runs on every animation frame. Build large constant data (like a full trajectory) outside `frame`, or once per scene.
- Keep the `scene` object stable (module constant or `useMemo`). A new object resets the animation clock.
- Axes are not equal-scaled. Set `aspect` to match your `bounds` when angles matter (a projectile, a circle).

## See also

- Player: https://ui.indoku.org/docs/player
- Overview: https://ui.indoku.org/docs/chart
