gridmesh API

← gridmesh · version 0.2.6

32 public items, in source order. Generated from the registry index, which re-derives them from the package source at publish time — so a signature here is the one that version actually ships.

pub struct CellRef { ck: integer, ix: integer }

A coord→cell-index reference, stored in the spatial-index hash keyed on the encoded coordinate ck.

pub fn enc_coord(x: integer, y: integer) -> integer

Encode an offset coord into one integer key. The hex world is small; ±2^20 per axis covers it with room to spare and stays within i64.

pub fn build_index(xs: const vector<integer>, ys: const vector<integer>) -> hash<CellRef[ck]>

Build the coord→cell-index spatial index for a placement-ordered cell list given as parallel (xs, ys). O(N), built once and reusable across neighbour/on-axis queries.

pub fn axial_dq(d: integer) -> integer

Axial unit-direction components for hex axis d (0..5), via 60°·d. Branch form avoids per-call vector-literal allocation.

pub fn axial_dr(d: integer) -> integer

The r half of that same direction. Read as a PAIR with axial_dq — one direction, two components, and neither is meaningful alone.

pub fn step_x(x: integer, y: integer, d: integer, k: integer) -> integer

Offset coord k steps from (x, y) along hex axis d.

pub fn step_y(_x: integer, y: integer, d: integer, k: integer) -> integer

The y half of the pair. Row parity does not reach it — a step along an axis moves whole rows — which is why this ignores x where step_x cannot.

pub fn idx_at(cidx: const hash<CellRef[ck]>, x: integer, y: integer) -> integer

snap index of the cell at (x, y), or -1 if none. (cidx not index — index is a reserved keyed-collection-type keyword in loft.)

pub fn nbr_count_idx(cidx: const hash<CellRef[ck]>, cx: integer, cy: integer) -> integer

Index-backed filled-neighbour count (distance 1) — the 6 axis-1 cells.

pub struct SegMesh { kinds: vector<u8>, x0s: vector<single>, y0s: vector<single>, z0s: vector<single>, x1s: vector<single>, y1s: vector<single>, z1s: vector<single>, colors: vector<u8>, cell_ix: vector<integer> }

── B1: SegMesh accumulator ──────────────────────────────────────────

Struct-of-arrays line-segment mesh for segment/ribbon consumers (the crystal rule). Each segment is a 3D line (x0,y0,z0)→(x1,y1,z1) with a kind, a colour, and the OWNING cell index (cell_ix) — the latter lets a consumer attribute a segment back to its cell (e.g. GPU bloom-from- centre keyed by cell age) and lets the dirty-rebuild find-and-replace a cell's segments. Coordinates are single (f32 — the GPU VBO format, no precision loss vs what is rendered) and kind/colour are u8 (M1 narrowing: ~60 % less memory than the i64/f64 prototype).

pub fn seg_mesh_new() -> SegMesh

An empty segment mesh: the nine parallel arrays emit_segment appends into. Flat and parallel rather than a vector of records, so a chunk rebuild concatenates (seg_mesh_append) instead of walking a structure.

pub fn emit_segment(m: SegMesh, kind: integer, x0: float, y0: float, z0: float, x1: float, y1: float, z1: float, color: integer, owner: integer)

Append one segment owned by cell owner. Callers pass float coords and integer kind/colour; they are narrowed to the storage width here.

pub fn seg_len(m: const SegMesh) -> integer

Number of segments in the mesh.

pub fn seg_mesh_append(dst: SegMesh, src: const SegMesh)

Append all of src's segments onto dst (concatenate the nine parallel arrays). Used to assemble a render-group mesh from its chunks' CACHED meshes and to concatenate chunk meshes into a full build — order-preserving, so the consumer can rely on a stable per-group draw order.

pub struct ChunkKey { ck: integer, cx: integer, cy: integer }

A chunk-space coordinate. ck = enc_coord(cx, cy) is its identity for dedup in the dirty index.

pub fn chunk_of(x: integer, y: integer, shift: integer) -> ChunkKey

The chunk owning offset coord (x, y) for a field with shift (chunk = 1<<shift cells per axis; shift 5 = the moros 32×32 chunk).

pub struct ChunkField { layout: integer, xs: vector<integer>, ys: vector<integer>, cidx: hash<CellRef[ck]>, chunk_shift: integer, halo_k: integer, dirty: hash<ChunkKey[ck]>, buckets: hash<ChunkBucket[ck]> }

A grid of cells partitioned into chunks: the cell coordinates, a coordinate-to-cell index, and the dirty chunks and per-chunk buckets kept up to date as cells are added. field_new makes one and field_add_cell grows it.

pub fn field_new(layout: integer, xs: vector<integer>, ys: vector<integer>, chunk_shift: integer, halo_k: integer) -> ChunkField

Build a field from placement-ordered coords. layout is 0 (odd-row offset coordinates, the only layout); chunk_shift makes a chunk 1 << chunk_shift cells a side; halo_k is how far across a border a rule may read. The dirty index starts empty; the spatial index (cidx) and the chunk->cell partition (buckets) are built once (O(N)) and maintained incrementally thereafter (field_add_cell), so a later collect_dirty_inputs is O(dirty), not O(N).

pub fn field_add_cell(f: ChunkField, x: integer, y: integer)

Edit — append a new cell (its index = old len(xs)), add it to its chunk's bucket (O(1) amortised), and mark its chunk (+ halo-border neighbours) dirty. Keeping the bucket current here is what makes collect_dirty_inputs O(dirty) rather than a full re-partition.

pub fn field_mark_dirty(f: ChunkField, x: integer, y: integer)

Edit — RECOLOUR an existing cell: mark its chunk (+ halo border) dirty without touching the coord arrays or index (the cell stays; only its consumer-side payload — colour/height — changed). Erase uses field_remove_cell instead (it must also drop the cell from the index).

pub fn field_remove_cell(f: ChunkField, x: integer, y: integer)

Edit — REMOVE an existing cell: drop it from the spatial index (cidx) and its chunk bucket, then mark its chunk (+ halo border) dirty. The MIRROR of field_add_cell, sharing mark_borders so a removal flows through the IDENTICAL incremental rebuild path as a paint. The coord slot in xs/ys is deliberately LEFT in place (not compacted): cell indices the field already handed out stay stable, so a consumer's parallel per-cell arrays (e.g. the projector's bloom births, indexed by cell index) are not invalidated. The orphaned slot is never read again — its index is in no bucket and no longer in cidx — and re-painting the same coord allocates a FRESH index (field_add_cell appends), so identity is never reused.

pub fn clear_dirty(f: ChunkField)

Clear the dirty index after a rebuild.

pub fn dirty_count(f: const ChunkField) -> integer

Number of chunks currently dirty (verification / O(dirty) sizing).

pub fn chunk_is_dirty(f: const ChunkField, x: integer, y: integer) -> boolean

Is the chunk owning (x, y) currently marked dirty?

pub struct ChunkInput { key: ChunkKey, cell_ixs: vector<integer>, halo_ixs: vector<integer> }

── B3: ChunkInput + per-chunk gather ─────────────────────────────────

What a per-cell RULE needs for ONE chunk: the indices of the cells it OWNS (emit geometry for these) + the ≤halo_k border-neighbour cells in OTHER chunks it may READ as context. COMPACT — no payload, and NO copy of the shared spatial index (cidx). The rule receives cidx SEPARATELY as a read-only ref (keyed params don't deep-copy) and par forwards it as a context arg, so the per-chunk working set stays small and cache-resident (copying the whole index per chunk would be O(N)).

pub fn all_inputs(f: const ChunkField, halo_k: integer) -> vector<ChunkInput>

One ChunkInput per chunk that holds cells (the full-build work-list). Reads the wired-in f.buckets (no re-partition).

pub fn collect_dirty_inputs(f: const ChunkField, halo_k: integer) -> vector<ChunkInput>

One ChunkInput per DIRTY chunk (the incremental-rebuild work-list). Iterates ONLY the dirty set and looks up each dirty chunk's bucket — so the cost is O(dirty chunks × their cells), independent of total N. A dirty chunk with no owned cells (a halo-marked empty neighbour) is skipped. Clear the dirty index after rebuilding.

pub struct GroupKey { gk: integer, gx: integer, gy: integer }

One render group: (gx, gy) its position in group coordinates (chunk coordinates divided by group_dim), gk the two encoded as one key. group_of answers it.

pub struct GroupInput { key: GroupKey, chunk_cks: vector<integer> }

The chunk ck's a group contains that actually hold cells (the assemble work-list). No mesh here — gridmesh partitions; the consumer owns meshes.

pub fn group_of(cx: integer, cy: integer, group_dim: integer) -> GroupKey

The group owning CHUNK coord (cx, cy): chunk-space divided by group_dim. Uses chunk_div so any group_dim works (not just powers of two) — a free dial.

pub fn all_groups(f: const ChunkField, group_dim: integer) -> vector<GroupInput>

One GroupInput per group that holds cells (the full-build work-list). O(chunks): each non-empty chunk dedups into its group's chunk list.

pub fn collect_dirty_groups(f: const ChunkField, group_dim: integer) -> vector<GroupInput>

One GroupInput per DIRTY group (the incremental re-upload work-list). A group is dirty if any of its chunks is dirty; its chunk_cks lists ALL its member chunks that hold cells (so the consumer reassembles the group's VBO from those chunks' CACHED meshes — unchanged chunks copied, not rebuilt).

FLAT COST: derives the dirty groups from f.dirty (O(dirty)), then for each enumerates its group_dim × group_dim member chunk coords analytically and probes f.buckets — O(dirty groups × group_dim²), independent of total N. (Never scans all chunks, which would make per-edit cost grow with N.)