# Radial bloom

A hub of telescoping rams pointed outward in one plane: closed it is an even star, driven out it is a ragged burst. Four guided stages per ram, and a vector of strokes drives each ram on its own.

> 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/radial-bloom.json
```

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

## Notes

- One ram is four concentric stages: a fixed sleeve and three that slide, each moving a third of the tip's travel. Six world units of overlap remain between consecutive stages at full extension, so no stage ever leaves the one guiding it.
- The rams have different strokes on purpose — twelve identical telescopes cannot nest around one hub. Closed, every tip sits at the same radius; it is the extension that is ragged.
- Rams alternate above and below the hub plane by pitch, so plan view is unchanged and the array opens into two cones as the camera tips. At pitch 0 the tipped views collapse to a line, which is what a flat array seen edge-on is.
- Stage positions, overlaps and every projection are solved. The hub's face detail and the collars at each stage mouth are drawn, not driven.
- The three moving stages are one painted member stepping down through three diameters, not four differently coloured parts: a telescope reads as one spoke that gets thinner, and the collar at each mouth is what says where the joints are.
- Non-finite strokes render that ram closed rather than clamped onto the ring — a missing reading is not a zero reading.

## Usage

```tsx
import { RadialBloom } from "@/components/ui/radial-bloom"

<RadialBloom rams={12} />

// The whole stroke, open and closed.
<RadialBloom behavior="bloom" />

// Controlled as one array, as a vector of rams, or as a dial you can pull open.
<RadialBloom extension={0.8} />
<RadialBloom strokes={[1, 0.4, 0.9, 0.2, 0.7, 0.55]} />
<RadialBloom interactive onExtensionChange={setExtension} />
```

## Props

| name | type | default | description |
| --- | --- | --- | --- |
| `view` | `"plan" | "front" | "profile" | "iso"` | `"plan"` | Where the camera stands. One array, four projections: straight down, straight on, side elevation, or three-quarter from above. |
| `extension` | `number` | — | Controlled extension of the whole array, 0 closed to 1 open. Supplying it stops the loop. |
| `strokes` | `number[]` | — | Controlled extension per ram, 0 to 1. Its length sets the ram count, so a six-value vector builds a six-ram hub. |
| `behavior` | `"bloom" | "ripple" | "index" | "flutter" | "static"` | `"flutter"` | The default holds station near full travel with a small dither, which is the array working. bloom runs the whole stroke open and closed, ripple sends a travelling wave round the ring, index drives one ram at a time with the rest parked back. |
| `rams` | `number` | `12` | How many rams on the hub, clamped to 0–24. Ignored when strokes is supplied. |
| `pitch` | `number` | `13` | Half-angle of the two ranks either side of the hub plane, in degrees, clamped to 0–40. Zero is a genuinely flat array. |
| `spin` | `number` | `0` | Turn of the whole array about its own axis, in degrees. Independent of view. |
| `speed` | `number` | `0.3` | Opens and closes, or passes round the ring, 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. |
| `interactive` | `boolean` | `false` | Drag out from the hub to pull the array open; arrows step 5%, with shift 15%, Home closes and End opens. |
| `onExtensionChange` | `(extension: number) => void` | — | Fired throughout a drag or a key press, in controlled mode too. |
| `signal` | `"idle" | "ready" | "warning"` | `"ready"` | Hub lamp: metal when idle, accent when ready, shell for a warning. |
| `showEnvelope` | `boolean` | — | Dashed circles at the closed and full reach radii. Defaults on in blueprint and off elsewhere. |
| `showGround` | `boolean` | `true` | Draw the contact shadow under the hub. |
| `label` | `string` | — | Caption under the array. |
| `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/radial-bloom.tsx`
