# Phyllotaxis geometry

The golden-angle disc, the Fibonacci spiral arms that fall out of it, a dished face and its normals, and the two-axis aim that points the whole thing at a light.

> 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/phyllotaxis-geometry.json
```

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

## Notes

- Pure functions over plain `{x, y}` and `{x, y, z}`. No React, no camera, no dependencies.
- The parastichy count is the test that matters: for any lattice from 80 to 600 florets, the two nearest-neighbour index steps are consecutive Fibonacci numbers. Nothing in the file knows that — it is what the angle does.
- There is no plant here, and no sun. `aimFrom` is a two-axis mount's inverse, not a solar position for a date and a latitude.

## Usage

```tsx
import { aimFrom, parastichyOffsets, trackerFrame, vogelDisc } from "@/lib/robocn/phyllotaxis"

const sites = vogelDisc(160, { radius: 28 })
parastichyOffsets(sites)          // [21, 34] — nobody chose those
const frame = trackerFrame(aimFrom({ x: 0.3, y: 0.8, z: -0.5 }))
```

## API

| name | type | default | description |
| --- | --- | --- | --- |
| `GOLDEN_ANGLE` | `number` | — | 137.507…°, the angle the whole lattice is one consequence of. |
| `vogelDisc(count, options?)` | `PhyllotaxisSite[]` | — | Sites at n·137.507° and radius √n, so every annulus of equal area holds the same number of them. `innerRadius` leaves the eye bare. |
| `parastichyOffsets(sites, families?)` | `number[]` | — | The index steps whose neighbours are closest — the spiral-arm families. They come out consecutive Fibonacci numbers because the angle says so. |
| `spiralArm(sites, start, step)` | `PhyllotaxisSite[]` | — | One arm: the chain of sites stepping by one of those offsets. |
| `discDish(site, options?)` | `DiscDishPoint` | — | The shallow paraboloid the florets sit in: the offset along the face normal, and the unit normal there. |
| `aimFrom(direction)` | `TrackerAim` | — | A direction turned into an azimuth and an elevation. A zero vector parks level rather than returning NaN. |
| `aimDirection(aim)` | `Vec3` | — | The inverse. Round trips exactly. |
| `trackerFrame(aim)` | `TrackerFrame` | — | The head's own axes in the world: `forward` is the aim, `right` stays level however far it pitches, and `right × forward = up`. |
| `framePoint(frame, origin, local)` | `Vec3` | — | A point written in the head's frame, placed in the world. Distance-preserving. |
| `rayFlorets(count, options)` | `RayFloret[]` | — | Petals hinged on a rim, in the disc's own frame. Rigid: the length is exact at every pitch. |

## Source

- `src/lib/robocn/phyllotaxis.ts`
