# Lidar scan

A polar range display for real or simulated lidar samples. Returns stay in sensor coordinates and rotate with its heading.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/lidar-scan.json
```

Registry item: `lidar-scan` · [`https://robocn.dev/r/lidar-scan.json`](https://robocn.dev/r/lidar-scan.json)

## Notes

- The component does not generate samples. The demo supplies a deterministic room outline and obstacle, clearly labelled as sample data.
- Negative, non-finite, and beyond-range distances are omitted. Non-finite angles are omitted. Zero-distance returns are valid and plot at the origin.
- Returns are drawn by age: each brightens as the ray passes it and fades over the next 150° of sweep, down to a floor that keeps the plot readable as a map between passes. That is display only — nothing is filtered. Supply a new samples array when fresh sensor data arrives.

## Usage

```tsx
import { LidarScan } from "@/components/ui/lidar-scan"

<LidarScan
  samples={[{ angle: 0, distance: 5 }, { angle: 90, distance: 8 }]}
  maxRange={10}
  heading={20}
  scanAngle={60}
  showRays
/>
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `samples` | `readonly LidarSample[]` | `[]` | Angle-distance pairs. Angles run clockwise from sensor-forward; distance uses the same units as maxRange. |
| `maxRange` | `number` | `10` | Distance represented by the outer ring. Non-finite or nonpositive values use 10. |
| `heading` | `number` | `0` | Clockwise sensor heading in degrees. Rotates samples and scan ray together; non-finite values use zero. |
| `scanAngle` | `number` | — | Scan-ray angle relative to sensor-forward. Omit it and the ray runs behavior; a non-finite value hides it. |
| `behavior` | `"sweep" | "pointer" | "static"` | `"sweep"` | Sweep the ray around, or aim it at the pointer. |
| `speed` | `number` | `0.35` | Sweeps per second. |
| `animate` | `boolean` | `true` | Off parks the machine at phase and stops rendering. A reduced-motion preference does the same. |
| `paused` | `boolean` | `false` | Freeze where it stands. |
| `phase` | `number` | `0` | Seconds of offset, so a row of machines breaks step. |
| `showRay` | `boolean` | `true` | Draw the scan ray and its wedge at all. |
| `interactive` | `boolean` | `false` | Hover a return to pick it out and read its bearing and distance off the bottom of the plot. |
| `onSampleHover` | `(sample: LidarSample | null) => void` | — | The return under the pointer, and null when it leaves. |
| `showRays` | `boolean` | `false` | Draw a line from the sensor to each valid return. |
| `showRings` | `boolean` | `true` | Quarter-range rings and crosshairs. |
| `label` | `string` | — | Bottom caption; defaults to the maximum range. |
| `variant` | `"solid" | "outline" | "blueprint" | "wire"` | `"solid"` | How the machine is painted. Geometry never changes between variants. |
| `size` | `"xs" | "sm" | "md" | "lg" | "xl" | number` | `"md"` | Rendered width in pixels, or a step on the scale. |
| `color` | `string` | `var(--robot-shell)` | Body panels — the colour the machine reads as. |
| `accent` | `string` | `var(--robot-accent)` | Status colour: tip light, live tool, readouts. |
| `metal` | `string` | `var(--robot-metal)` | Bare machined parts: collars, bolts, tool bodies. |
| `dark` | `string` | `var(--robot-dark)` | Cast joints, base, shadow side. |
| `palette` | `Partial<RobotPalette>` | — | Override any subset of roles at once, including glow and grid. |

## Source

- `src/components/ui/lidar-scan.tsx`
