# Headless API

The `glyphcss` vanilla package exposes a glyph-shaped imperative API plus the raw
`@glyphcss/core` math primitives.

## `createGlyphScene`

The main entry point. Creates and mounts a scene into a host element and returns a
`GlyphSceneHandle` with methods to add meshes, hotspots, and update options.
**Always construct a camera first and pass it in.**

```ts
import { createGlyphCamera, createGlyphScene } from "glyphcss";

const host = document.querySelector<HTMLElement>("#scene")!;

const camera = createGlyphCamera({ rotX: 25, zoom: 50 });

const scene = createGlyphScene(host, {
  camera,
  mode: "solid",            // "wireframe" | "solid" | "voxel" | "ink"
  cols: 100,                // grid width in character columns
  rows: 30,                 // grid height in character rows
  cellAspect: 2.0,          // cell height ÷ width (typical monospace: ~2.0)
  glyphPalette: "default",  // named glyph palette
  useColors: true,          // emit color <span>s in output
  directionalLight: { direction: [0.5, 0.7, 0.5], intensity: 1 },
  ambientLight: { intensity: 0.4 },
});
```

### `.add(polygons, transform?)`

Register a `Polygon[]` as a mesh. Returns a `GlyphMeshHandle`.

```ts
import { cubePolygons } from "@glyphcss/core";

const meshHandle = scene.add(
  cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" }),
  {
    position: [1, 0, 0],     // translate in world space
    scale: 0.8,              // uniform scale (or [sx, sy, sz])
    rotation: [0, 45, 0],    // XYZ Euler angles in degrees
  }
);

// Later, update the transform and re-render:
meshHandle.setTransform({ position: [2, 0, 0] });
scene.rerender();

// Replace the mesh geometry in place:
meshHandle.setPolygons(nextPolygons);

// Remove the mesh:
meshHandle.dispose();
```

### `.setOptions(partial)`

Update any scene option. Triggers an automatic re-render.

```ts
scene.setOptions({
  mode: "wireframe",
  cols: 120,
  rows: 36,
  directionalLight: { direction: [1, 0.5, 0.2], intensity: 1.2 },
});
```

### `.destroy()`

Remove the scene DOM and clear all registered meshes and hotspots.

```ts
scene.destroy();
```

## End-to-end example

The following snippet covers `add`, `setOptions`, `addHotspot`, and `destroy` in
one runnable block:

```ts
import {
  createGlyphCamera,
  createGlyphScene,
  createGlyphOrbitControls,
} from "glyphcss";
import { cubePolygons, octahedronPolygons, axesHelperPolygons } from "@glyphcss/core";

const host = document.querySelector<HTMLElement>("#scene")!;

// 1. Create the camera first.
const camera = createGlyphCamera({ rotX: 25, zoom: 50 });

// 2. Create the scene with the camera.
const scene = createGlyphScene(host, { camera, mode: "solid", cols: 100, rows: 30 });

// 3. Add meshes. Each call returns a handle.
const cubeHandle = scene.add(
  cubePolygons({ center: [-1, 0, 0], size: 0.8, color: "#4488ff" })
);
const octaHandle = scene.add(
  octahedronPolygons({ center: [1, 0, 0], size: 0.8, color: "#ffcc44" })
);
scene.add(axesHelperPolygons({ size: 1.5 }));

// 4. Add a hotspot.
const hotspot = scene.addHotspot(
  { id: "octa-top", at: [1, 0.8, 0], size: [4, 2] },
  () => alert("octahedron top"),
);

// 5. Attach orbit controls.
const controls = createGlyphOrbitControls(scene, { drag: true, wheel: true });

// 6. Update options at any time.
scene.setOptions({ mode: "wireframe" });

// 7. Move a mesh.
cubeHandle.setTransform({ position: [-2, 0, 0] });
scene.rerender();

// 8. Cleanup everything.
hotspot.remove();
controls.destroy();
octaHandle.dispose();
cubeHandle.dispose();
scene.destroy();
```

## Camera factories

Two cameras: orthographic (default, `createGlyphCamera`) and perspective.
First-person view is `createGlyphPerspectiveCamera` + `createGlyphFirstPersonControls`.

```ts
import {
  createGlyphCamera,
  createGlyphPerspectiveCamera,
  createGlyphOrthographicCamera,
} from "glyphcss";

// Orthographic — default alias, best for voxel/iso scenes
const camera = createGlyphCamera({ rotX: 25, zoom: 50 });

// Orthographic — explicit form
const ortho = createGlyphOrthographicCamera({ rotX: 25, zoom: 50 });

// Perspective — foreshortened; required for first-person controls
const perspective = createGlyphPerspectiveCamera({
  rotX: 25, rotY: 0,
  distance: 3, zoom: 50, stretch: 0.95,
});

// All camera properties are mutable after creation:
perspective.rotY = 45;
perspective.distance = 4;

// Swap the camera on a running scene:
scene.setOptions({ camera: ortho });
```

## Lower-level pieces

`@glyphcss/core` is browser-agnostic (no DOM globals) and can run in Node or a
web worker. The rasterizer itself lives in `glyphcss` — it is pure too (geometry
+ camera → string), it just ships with the renderer rather than with core.

```ts
import { icosahedronPolygons } from "@glyphcss/core";
import {
  buildRasterizeContext,
  rasterize,
  projectHotspots,
  createGlyphPerspectiveCamera,
} from "glyphcss";

const polygons = icosahedronPolygons({ center: [0, 0, 0], size: 1, color: "#44ffcc" });

const camera = createGlyphPerspectiveCamera({ rotX: 25, zoom: 50 });
const grid = { cols: 160, rows: 48, cellAspect: 2.0 };

// `RasterizeContextOptions` — `polygons` is required for solid/voxel, and
// wireframe edges are derived from it when `wireframe` is omitted.
const ctx = buildRasterizeContext({ camera, grid, polygons, mode: "solid" });

// Rasterize one frame to a string:
const text = rasterize(ctx);

// Project hotspots to grid cells:
const cells = projectHotspots(
  [{ id: "top", at: [0, 1, 0] }],
  camera,
  160,
  48,
  2.0,
);
// cells is { id, col, row, depth, visible }[]
```

`@glyphcss/core` owns the geometry and math — polygon generators (~44 named
shapes plus `resolveGeometry`), parsers, mesh ops, and `buildSceneContext({
polygons })` for scene composition. It does not own the rasterizer.

## Static compile

`compileScene` renders a scene to its `<pre>` string with **no DOM** — same
defaults as `createGlyphScene`, byte-identical output. The foundation of the
build-time toolchain (see [Compiling to Static](/guides/compile)).

```ts
import { compileScene } from "glyphcss";

const { html, inner, cols, rows } = compileScene({ polygons, cols: 80, rows: 24 });
```

For interactive exports, `buildGlyphInteractiveExport(polygons, { interactions })`
(pure, browser-safe) emits a self-contained snippet shipping only the declared
control + a decimated mesh; `glyphCodepenPrefill` turns it into a CodePen POST.
Mesh simplification is available standalone as `decimatePolygons(polygons, { grid })`
from `@glyphcss/core`.

The Node file pipeline (`loadMeshFromFile`, `compileFile`, the Vite plugin, the
`glyphcss` CLI) lives in `@glyphcss/compile`.
