← hex_fit · API reference · version 0.1.5
5 file(s), 1166 lines. This is the source the published version ships, and it is loft — the library is written in the same language you call it from.
Public items
A worked example is a call site the author chose as the thing to read first. Call sites are every line naming the item, found mechanically over the source with comments excluded — so an item with no worked example is not an item nobody uses.
| Item | Defined | Worked example | Call sites |
|---|---|---|---|
Draft struct | 42 | — | 53 54 59 82 93 112 … (11 total) |
draft_new fn | 53 | — | 82 144 238 246 249 250 … (16 total) |
draft_has_run fn | 59 | — | 187 |
draft_write fn | 93 | HXI-001 | 259 124 125 188 |
draft_read fn | 112 | HXI-003 | 185 191 193 196 199 203 … (14 total) |
draft_draw fn | 149 | — | 79 111 114 156 164 |
draft_interior fn | 179 | — | 210 |
draft_rebuild_text fn | 207 | HXI-002 | 80 120 121 158 168 169 |
draft_canon fn | 236 | HXI-001 | 259 |
draft_canon_text fn | 258 | HXI-001 | 80 129 130 134 |
FIT_OK const | 274 | — | 285 325 331 105 149 215 … (28 total) |
FIT_NO_FORM const | 275 | — | 286 324 |
FIT_BAD_DIR const | 276 | — | 287 326 239 |
FIT_BAD_P const | 277 | — | 288 327 242 |
FIT_NO_VERTEX const | 278 | — | 289 328 247 248 |
FIT_NOT_LEGAL const | 279 | — | 290 329 |
FIT_OUTSIDE const | 280 | — | 291 330 248 253 257 |
draft_fit_reason fn | 284 | — | 76 |
draft_fits fn | 323 | HXI-004 | 77 106 107 239 242 247 … (10 total) |
draft_fit_p fn | 339 | HXI-004 | 254 |
FIT_NO_SIDE const | 48 | — | 58 103 316 |
FIT_BAD_T const | 49 | — | 59 106 306 |
FIT_BAD_SPAN const | 50 | — | 62 104 314 315 |
FIT_BAD_SHELL const | 51 | — | 63 150 332 |
FIT_BAD_HEIGHT const | 52 | — | 66 274 54 56 79 410 … (7 total) |
FIT_BAD_MAT const | 53 | — | 73 236 372 373 374 |
FIT_BAD_SCALE const | 54 | — | 70 272 75 76 77 78 … (7 total) |
fit_reason fn | 57 | — | 19 78 79 77 248 249 |
feature_grid fn | 88 | HXI-005 | 101 113 282 283 |
feature_fits fn | 100 | HXI-005 | 305 306 314 315 316 |
feature_fit_t fn | 112 | — | 127 307 308 |
feature_fit_residual fn | 126 | — | 309 311 |
arc_is_shell fn | 139 | — | 149 27 |
arc_fits fn | 148 | — | 13 14 18 331 332 |
arc_fit_n fn | 161 | HXI-006 | 178 26 334 346 |
arc_fit_residual fn | 177 | — | 28 335 |
arc_snap_n fn | 191 | HXI-006 | 200 336 342 |
arc_snap_residual fn | 200 | — | 337 |
level_fits fn | 214 | HXI-008 | 36 37 402 403 404 |
MAT_MAX const | 225 | — | 53 236 371 372 379 380 … (10 total) |
mat_fits fn | 235 | HXI-007 | 370 371 372 373 374 |
HEIGHT_SCALE const | 250 | — | 290 293 296 299 302 65 … (8 total) |
height_units_at fn | 253 | — | 273 280 290 67 |
height_from_units_at fn | 261 | — | 273 280 293 |
seat_fits_at fn | 271 | — | 296 54 55 56 57 65 … (9 total) |
seat_fit_z_at fn | 278 | — | 285 299 60 61 66 |
seat_fit_residual_at fn | 285 | — | 302 62 |
height_units fn | 290 | — | 43 67 408 |
height_from_units fn | 293 | — | 43 409 |
seat_fits fn | 296 | HXI-008 | 305 65 407 410 413 |
seat_fit_z fn | 299 | — | 66 411 414 |
seat_fit_residual fn | 302 | — | 412 |
seat_is_integral fn | 305 | — | 415 |
src/hex_fit.loft
// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// hex_fit — the doorstep — refuse at authoring time what would not round-trip.
//
// `fits?` and `snap`. A continuous model parameter must be quantised to what the field
// distinguishes; off that grid a value is silently SNAPPED, not rejected, so the doorstep
// refuses it up front with a named reason, an offer of the nearest fitting alternative, and the
// residual. Also the `Draft` — a form carrying an embedded wall run.
//
// Entry point: re-exports `hexfit`, `hexdraft`.
pub use hexfit::*;
pub use hexdraft::*;
src/hexdraft.loft
// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// hexdraft — A STENCIL DESCRIPTION THAT CARRIES LINEWORK, AND A ROUND TRIP THAT NOTICES (`OD-13`).
//
// A stencil needs linework in all 24 directions to describe a believable city or castle. A round
// trip that recovers only the form drops the embedded run, and then reports success on a model
// missing its content. So a `Draft` is a **form plus an embedded run**: it writes and reads as one
// text, it draws as one field, and it rebuilds from that field **including the run** — so a
// dropped run fails the trip instead of passing it.
//
// THE SLOT DISCIPLINE, unchanged from `X51`/`X58`/`X59`/`X60`: the run is a material on **interior**
// edges (both cells in the footprint), so the footprint is untouched and the form recovery reads a
// slot the run never writes. The two recoveries read disjoint slots and compose without
// interacting — which is why this is wiring rather than redesign.
//
// ⚠ ONE NEIGHBOUR CONVENTION: `hex_neighbor` with `hex_edge_corners`, never `nb_q`/`nb_r` — the
// two number neighbours differently, and mixing them marks the wrong edge (`SPEC` **L11**).
//
// ⚠ NAME: this is the **description** side — a `Form` plus linework, the thing a canonical text
// spells. `hex_field::Stencil` is the **field** side (cells, heights, edges, layers, with
// rotate/mirror/stamp). Both are legitimate and they are exactly `OD-6`'s two halves — *"is a
// stencil a field or a generative description?"*, answered *"the foxel is the stored truth; a
// description is admissible only if it draws into the schema exactly"*. They must not share a
// name, so this one is `Draft`.
//
// SCOPE: **one** embedded run per stencil. Several would need the interior edges split into
// connected components first, which is a separate step and is not pretended here.
//
// AND ONE SPELLING PER MODEL: a model must have exactly ONE text or a `diff` stops meaning *the
// model changed*. The form half gets that from `form_canon`; a run has two spellings that mark the
// identical edges, and `draft_canon` picks by asking the field which one it answers with.
use hex_field::*;
use hex_grid::*;
use hex_form::*;
use hex_recover::*;
use hex_draw::*;
use hex_shape::*;
// A form plus an embedded run, written, read and drawn as one: the form, the run's direction
// (`df_d`, -1 for none), its anchor `(df_a, df_b)` and its periods. `draft_new`,
// `draft_read` and `draft_canon` make one.
pub struct Draft {
pub df_form: Form,
pub df_d: integer, // embedded run direction, or -1 for none
pub df_a: integer, // anchor, triangle coordinates
df_b: integer,
pub df_p: integer, // periods
}
// A stencil description: the form, plus the linework run it carries (`d` = -1 for none). The
// run is part of the value rather than something a caller keeps beside it, so a rebuild that
// drops it is caught by the text diff.
pub fn draft_new(f: Form, d: integer, a: integer, b: integer, p: integer) -> Draft {
Draft { df_form: f, df_d: d, df_a: a, df_b: b, df_p: p }
}
// Does this draft carry a linework run? Check before reading the run's fields, which are
// only meaningful when it does.
pub fn draft_has_run(s: Draft) -> boolean { s.df_d >= 0 }
fn nth_word(line: text, i: integer) -> text {
ws = line.split(' ');
ws[i] ?? ""
}
fn word_int(line: text, i: integer) -> integer { nth_word(line, i) as integer ?? 0 }
// How many space-separated fields the line has. The layout fixes this per record, so a line
// carrying MORE is a different line — trailing junk and a trailing space both land here.
fn word_count(line: text) -> integer { len(line.split(' ')) }
// Is field `i` an integer SPELLED THE WAY `draft_write` spells one? Not a character class: the
// parse has to reproduce the original characters, which refuses ``, `x`, `007` and `+1` in one
// test — see `draft_read`.
fn word_is_int(line: text, i: integer) -> boolean {
w = nth_word(line, i);
v = w as integer ?? 0;
"{v}" == w
}
// A refusal, in the one shape this reader has for one: a stencil with an EMPTY form. Nothing in
// `-> Draft` says the parse can fail, so the caller's check is `form_sides(s.df_form) == 0`.
fn draft_refused() -> Draft { draft_new(form_new(0, [], []), 0 - 1, 0, 0, 0) }
// The text of a stencil: the form's spelling, then — if there is one — a single `wall` line.
// One line, one record, same shape as `side`: the reader refuses anything else rather than
// repairing it (`X39`).
//
// ⚠ THIS IS NOT THE CANONICAL TEXT. It writes the spelling it is GIVEN, and both the form's
// start corner and the run's direction are choices the model does not fix — so two calls can
// spell one stencil two ways. A round trip diffs against `draft_canon_text`.
//
// Example: @HXI-001
pub fn draft_write(s: Draft, name: text) -> text {
base = form_write(s.df_form, name);
if s.df_d < 0 { return base; }
base + "\nwall d {s.df_d} a {s.df_a} b {s.df_b} p {s.df_p}"
}
// Parse. The `side` lines go to `form_read` unchanged; a trailing `wall` line is this layer's.
// A malformed record yields a stencil with an empty form — REFUSED, never repaired.
//
// It refuses exactly what `draft_write` cannot emit: a trailing space, an extra field, `+3`,
// `003`, a field that is not a number at all, or a `wall` line that is not last. Repairing any
// of those would admit a model nobody wrote — `wall d x …` read as `d 0` is a wall pointing
// somewhere else.
//
// The rule is a RE-SPELLING IDENTITY: a field is an integer only if printing the
// parse reproduces the original characters, a line is a record only if it carries exactly the
// fields the writer emits, and the `wall` line is LAST because that is where the writer puts it.
//
// Example: @HXI-003
pub fn draft_read(t: text) -> Draft {
lines = t.split('\n');
nl = len(lines);
ftext = "";
d = 0 - 1;
a = 0;
b = 0;
p = 0;
nwall = 0;
for i in 0..nl {
ln = lines[i] ?? "";
if nth_word(ln, 0) == "wall" {
nwall = nwall + 1;
if i != nl - 1 { return draft_refused(); } // the writer appends it LAST
if word_count(ln) != 9 { return draft_refused(); } // `wall d <d> a <a> b <b> p <p>`
if nth_word(ln, 1) != "d" || nth_word(ln, 3) != "a" || nth_word(ln, 5) != "b"
|| nth_word(ln, 7) != "p" {
return draft_refused();
}
if !word_is_int(ln, 2) || !word_is_int(ln, 4) || !word_is_int(ln, 6)
|| !word_is_int(ln, 8) {
return draft_refused(); // integers, spelled as we spell them
}
d = word_int(ln, 2);
a = word_int(ln, 4);
b = word_int(ln, 6);
p = word_int(ln, 8);
} else {
if i == 0 { ftext = ln; } else { ftext = ftext + "\n" + ln; }
}
}
if nwall > 1 { return draft_refused(); }
draft_new(form_read(ftext), d, a, b, p)
}
// Draw the whole stencil into one field: the fill, its own boundary walls, and the embedded run as
// material on INTERIOR edges only. Returns the number of interior edges the run marked.
pub fn draft_draw(s: Draft, cq: integer, cr: integer, cells: HexSet, edges: EdgeSet,
wallmat: integer, embedmat: integer) -> integer {
form_fill(s.df_form, cq, cr, cells);
draw_walls(cells, edges, wallmat);
if s.df_d < 0 { return 0; }
q0 = hexset_q0(cells);
r0 = hexset_r0(cells);
gw = hexset_w(cells);
gh = hexset_h(cells);
ew = edgeset_new(q0, r0, gw, gh);
w = wall_from_run(s.df_d, s.df_a, s.df_b, s.df_p, embedmat);
wall_write(w, ew, q0, r0, gw, gh);
n = 0;
for r in r0..r0 + gh {
for q in q0..q0 + gw {
for dd in 0..3 {
(nq, nr) = hex_neighbor(q, r, dd);
if edge_mat(ew, q, r, nq, nr) != 0
&& hexset_get(cells, q, r) && hexset_get(cells, nq, nr) {
edge_set_mat(edges, q, r, nq, nr, embedmat);
n = n + 1;
}
}
}
}
n
}
// The INTERIOR edges carrying `embedmat`, isolated — what the run reader consumes. Boundary edges
// (one cell out) are the stencil's own walls and are never part of a run.
pub fn draft_interior(cells: HexSet, edges: EdgeSet, embedmat: integer) -> EdgeSet {
q0 = hexset_q0(cells);
r0 = hexset_r0(cells);
gw = hexset_w(cells);
gh = hexset_h(cells);
ei = edgeset_new(q0, r0, gw, gh);
for r in r0..r0 + gh {
for q in q0..q0 + gw {
for dd in 0..3 {
(nq, nr) = hex_neighbor(q, r, dd);
if edge_mat(edges, q, r, nq, nr) == embedmat
&& hexset_get(cells, q, r) && hexset_get(cells, nq, nr) {
edge_set_mat(ei, q, r, nq, nr, embedmat);
}
}
}
}
ei
}
// REBUILD, INCLUDING THE RUN. The form comes from the cells (constructive recovery, `X45`); the
// run comes from the interior edges (`wall_read_run`, `X60`) — two recoveries reading disjoint
// slots. An unreadable run is REFUSED, so it is dropped loudly by a text diff rather than silently.
//
// ⚠ DIFF THIS AGAINST `draft_canon_text`, NOT against `draft_write`. What comes back is the
// spelling the FIELD answers with, and for the run half that is one of two — see the note there.
//
// Example: @HXI-002
pub fn draft_rebuild_text(cells: HexSet, edges: EdgeSet, name: text, embedmat: integer) -> text {
base = rebuild_construct_text(cells, name);
if base == "" { return ""; }
ei = draft_interior(cells, edges, embedmat);
(d, a, b, p, ok) = wall_read_run(ei, hexset_q0(cells), hexset_r0(cells),
hexset_w(cells), hexset_h(cells));
if !ok { return base; }
base + "\nwall d {d} a {a} b {b} p {p}"
}
// ── ONE MODEL, ONE TEXT — the canonical spelling ───────────────────────────
//
// The format's premise is that a `diff` means *the model changed*, which needs every model to
// have exactly ONE text. The form half has that: a shape has n cyclic spellings and `form_canon`
// picks the smallest, which is why `rebuild_construct_text` and `form_canon_text` agree.
//
// THE RUN HALF HAD NO SUCH RULE, and a run has exactly TWO spellings — `(d, A, p)` and
// `(d + 12, B, p)` — because A-to-B and B-to-A mark the identical edges and the field stores no
// orientation (`wall_read_run`'s own note). Measured over all 24 directions: the two spellings
// draw fields that differ in ZERO edges, and **exactly one of them survives a text round trip**,
// never both and never neither. So an author who wrote the wall from the other end got *the
// model changed* out of a byte diff on a model that had not changed at all.
//
// The canonical spelling is therefore READ OFF THE FIELD rather than chosen by a rule — the same
// discipline `hexfit`'s feature grid is built on (`SPEC` **L11**: consult the table that owns the
// answer), and the only definition that cannot drift from what `draft_rebuild_text` will answer,
// since it is that same reader.
//
// Example: @HXI-001
pub fn draft_canon(s: Draft, cells: HexSet) -> Draft {
cf = form_canon(s.df_form);
if s.df_d < 0 { return draft_new(cf, 0 - 1, 0, 0, 0); }
q0 = hexset_q0(cells);
r0 = hexset_r0(cells);
gw = hexset_w(cells);
gh = hexset_h(cells);
ew = edgeset_new(q0, r0, gw, gh);
w = wall_from_run(s.df_d, s.df_a, s.df_b, s.df_p, 1);
if wall_write(w, ew, q0, r0, gw, gh) <= 0 {
return draft_new(cf, s.df_d, s.df_a, s.df_b, s.df_p);
}
(rd, ra, rb, rp, ok) = wall_read_run(ew, q0, r0, gw, gh);
if !ok { return draft_new(cf, s.df_d, s.df_a, s.df_b, s.df_p); }
draft_new(cf, rd, ra, rb, rp)
}
// The one text a round trip can be diffed against. A run that does not FIT is left as written —
// the canon is a spelling rule, not a doorstep, and refusing here would hide which of the two is
// wrong.
//
// Example: @HXI-001
pub fn draft_canon_text(s: Draft, cells: HexSet, name: text) -> text {
draft_write(draft_canon(s, cells), name)
}
// ── THE DOORSTEP (`fits?`, law C₁ / `K-FIT`) ───────────────────────────────
//
// `X50`/`I-QUANT`: a value off the grid the field distinguishes is **silently snapped, not
// rejected** — so the refusal has to happen at authoring time or the round trip reports success on
// a model nobody wrote. `K-FIT` also fixes the FORM of a refusal: it **names its restriction** and
// **offers the nearest fitting alternative**; never a silent snap, never a blank no.
//
// An embedded run inherits the line doorstep (§10.10: anchor on a hex VERTEX, a whole number of
// periods, endpoint also a vertex — `wall_run_ok`) and adds one requirement world linework does not
// have: **it must lie wholly INSIDE the footprint**. A run that pokes out is not an embedded wall
// at all — part of it lands on boundary edges, which belong to the stencil's own outline, and the
// reader would recover something the author did not write.
pub const FIT_OK = 0;
pub const FIT_NO_FORM = 1; // the form itself is not admissible (closed ∧ simple ∧ convex)
pub const FIT_BAD_DIR = 2; // the direction is not one of the 24
pub const FIT_BAD_P = 3; // a run of zero or negative periods
pub const FIT_NO_VERTEX = 4; // the anchor is a hex CENTRE, not a vertex
pub const FIT_NOT_LEGAL = 5; // whole periods, but the endpoint is not a vertex
pub const FIT_OUTSIDE = 6; // the run leaves the footprint
// The FIT_ code as a sentence, for showing a person why a draft did not fit. Keep the
// code for logic and use this only at the edge, so the reason never becomes the test.
pub fn draft_fit_reason(code: integer) -> text {
if code == FIT_OK { return "ok"; }
if code == FIT_NO_FORM { return "the form is not admissible (closed, simple, convex)"; }
if code == FIT_BAD_DIR { return "the direction is not one of the 24"; }
if code == FIT_BAD_P { return "a run needs at least one period"; }
if code == FIT_NO_VERTEX { return "the anchor is a hex centre; a run must start on a vertex"; }
if code == FIT_NOT_LEGAL { return "that many periods does not end on a vertex"; }
if code == FIT_OUTSIDE { return "the run leaves the footprint; an embedded wall must be interior"; }
"unknown"
}
// Every marked edge of the run is interior to `cells` — the containment test, in one place so the
// doorstep and the reader cannot disagree about what "inside" means.
fn run_is_inside(cells: HexSet, d: integer, a: integer, b: integer, p: integer) -> boolean {
q0 = hexset_q0(cells);
r0 = hexset_r0(cells);
gw = hexset_w(cells);
gh = hexset_h(cells);
ew = edgeset_new(q0, r0, gw, gh);
w = wall_from_run(d, a, b, p, 1);
n = wall_write(w, ew, q0, r0, gw, gh);
if n <= 0 { return false; }
ins = 0;
for r in r0..r0 + gh {
for q in q0..q0 + gw {
for dd in 0..3 {
(nq, nr) = hex_neighbor(q, r, dd);
if edge_mat(ew, q, r, nq, nr) != 0
&& hexset_get(cells, q, r) && hexset_get(cells, nq, nr) { ins = ins + 1; }
}
}
}
ins == n
}
// THE DOORSTEP. `cells` is the form's own footprint. Returns a reason code — `FIT_OK` or the
// specific restriction, never a bare false.
//
// Example: @HXI-004
pub fn draft_fits(s: Draft, cells: HexSet) -> integer {
if !form_admissible(s.df_form) { return FIT_NO_FORM; }
if s.df_d < 0 { return FIT_OK; }
if s.df_d >= 24 { return FIT_BAD_DIR; }
if s.df_p <= 0 { return FIT_BAD_P; }
if !tri_is_vertex(s.df_a, s.df_b) { return FIT_NO_VERTEX; }
if !wall_run_ok(s.df_d, s.df_a, s.df_b, s.df_p) { return FIT_NOT_LEGAL; }
if !run_is_inside(cells, s.df_d, s.df_a, s.df_b, s.df_p) { return FIT_OUTSIDE; }
FIT_OK
}
// AND THE ALTERNATIVE THE REFUSAL OWES (`K-FIT`): the longest run from this anchor, in this
// direction, that IS admissible and fits — searching downward so the offer is never longer than
// what was asked for. 0 when nothing fits, which is itself a usable answer: move the anchor.
//
// Example: @HXI-004
pub fn draft_fit_p(s: Draft, cells: HexSet) -> integer {
if s.df_d < 0 || s.df_d >= 24 { return 0; }
if !tri_is_vertex(s.df_a, s.df_b) { return 0; }
want = s.df_p;
if want < 1 { want = 1; }
for k in 0..want {
try = want - k;
if try >= 1 && wall_run_ok(s.df_d, s.df_a, s.df_b, try)
&& run_is_inside(cells, s.df_d, s.df_a, s.df_b, try) {
return try;
}
}
0
}
src/hexfit.loft
// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// hexfit — THE DOORSTEP, for the parameters `draft_fits` does not cover.
//
// `SPEC` **K-FIT**: *"`fits?(m)` and `snap(m)` are ONE chokepoint, consulted by the round-trip gate
// AND by the editor. The limit sits at the doorstep: the editor refuses at authoring time — never a
// warning, never a downstream check. A refusal NAMES its restriction and offers the nearest fitting
// alternative with its residual — never a silent snap, never a blank rejection."*
//
// `hexdraft::draft_fits` implements that for the turtle form and one embedded run. This module
// covers the rest — features, arcs, levels, material ids and heights — so `I-QUANT` has a doorstep
// everywhere it has a grid.
//
// THE RULE THIS MODULE IS BUILT ON, and it is not the obvious one: **the admissible set is READ OFF
// THE FIELD, never computed from a formula.** `X48` recorded a feature's grid as the edge centres
// `(2i+1)/2n`. Measured, that is true for only ONE of the two side families: the sides whose edges
// tile the whole side. On the other family the edges are INSET — they span exactly `√3/2` of the
// side, uniformly spaced by `(√3/2)/(n−1)` — so `(2i+1)/2n` would refuse every legal position and
// offer illegal ones. Consulting `side_edges` cannot make that mistake, and it is the same lesson
// as `SPEC` **L11**: consult the table that owns the answer, never keep a private copy of it.
//
// AND WHERE THERE IS NO GRID, THIS MODULE SAYS SO. A doorstep that refuses more than the field
// distinguishes is worse than none — it makes legal models unauthorable. Levels and terrain are
// measured to have nothing to refuse, and `level_fits` / `seat_fits` say that in code rather than
// inventing a restriction to look thorough.
// ── Using it ─────────────────────────────────────────────────────────────────
//
// Everything here answers one shape of question: DOES THIS MEASUREMENT LAND ON THE GRID,
// and if not, what is the nearest one that does? Each quantity has the same three:
//
// u = height_units(z); // to the grid's own units
// z2 = seat_fit_z(z); // the nearest seating height
// d = seat_fit_residual(z); // how far off it was — 0.0 means it already fitted
//
// Ask the residual when you want to WARN rather than move the value: a builder that snaps
// silently loses the author's intent, and one that refuses cannot place anything.
//
// The `*_at` twins take the scale explicitly, for a consumer working at a scale other than
// the default; the short names are those twins at `HEIGHT_SCALE`.
use hex_field::*;
use hex_draw::*;
use hex_shape::*;
use hexdraft::*;
use hex_form::*;
// Continuing `hexdraft`'s codes (0..6), so one `fit_reason` covers the whole doorstep.
pub const FIT_NO_SIDE = 7; // the side index is not a side, or carries no edges
pub const FIT_BAD_T = 8; // a feature `t` that is not one of that side's edge centres
pub const FIT_BAD_SPAN = 9; // a feature covering zero edges, or more than the side has
pub const FIT_BAD_SHELL = 10; // an arc radius that is not a realisable shell (3k^2 + m^2)
pub const FIT_BAD_HEIGHT = 11; // a height off the voxel's integer grid — it would truncate
pub const FIT_BAD_MAT = 12; // a material id the slot cannot hold (see MAT_MAX)
pub const FIT_BAD_SCALE = 13; // a height quantum that is not positive — there is no grid
// One reason table for the whole doorstep — `hexdraft`'s codes and these.
pub fn fit_reason(code: integer) -> text {
if code == FIT_NO_SIDE { return "that side has no edges to carry a feature"; }
if code == FIT_BAD_T {
return "a feature sits at an edge centre; that t is between two edges and would snap";
}
if code == FIT_BAD_SPAN { return "a feature covers at least one edge and at most the side"; }
if code == FIT_BAD_SHELL {
return "that radius is not a realisable shell; the disk would be the next shell down";
}
if code == FIT_BAD_HEIGHT {
// ⚠ THE QUANTUM IS THE CALLER'S, so this cannot name a number — see `seat_fits_at`.
return "the voxel stores height as a whole number of units; that value would truncate";
}
if code == FIT_BAD_SCALE {
return "a height quantum must be positive; there is no grid to test a value against";
}
if code == FIT_BAD_MAT {
return "that material id is outside the wall slot's range and would erase the wall";
}
draft_fit_reason(code)
}
// ── FEATURES ────────────────────────────────────────────────────────────────
//
// `X48`: the model stores `(side, t)`; the field stores a material on an EDGE. So `t` is
// recoverable only where an edge centre already is, and every other `t` snaps silently.
// The admissible positions on a side, in the order the run stores them — which is NOT t order
// (`A5`: index a feature by its exact `t`, never by its position in the `SideRun`).
//
// Example: @HXI-005
pub fn feature_grid(p: Plan, cells: HexSet, side: integer) -> vector<float> {
out: vector<float> = [];
if side < 0 || side >= 4 { return out; }
sr = side_edges(p, cells, side);
for i in 0..side_run_len(sr) { out += [sr.sr_t[i] ?? 0.0]; }
out
}
// THE DOORSTEP. `t` must be exactly an edge centre of this side — no tolerance (`P4`), because in
// regime R1 an epsilon is a defect signal and not a knob.
//
// Example: @HXI-005
pub fn feature_fits(p: Plan, cells: HexSet, side: integer, t: float, nedges: integer) -> integer {
g = feature_grid(p, cells, side);
n = len(g);
if n <= 0 { return FIT_NO_SIDE; }
if nedges <= 0 || nedges > n { return FIT_BAD_SPAN; }
for i in 0..n { if (g[i] ?? -1.0) == t { return FIT_OK; } }
FIT_BAD_T
}
// AND THE ALTERNATIVE THE REFUSAL OWES: the nearest admissible `t`. This is what an editor calls
// with a raw mouse position — the snap is EXPLICIT and returns a value the author can see, rather
// than happening silently inside `place_opening`.
pub fn feature_fit_t(p: Plan, cells: HexSet, side: integer, t: float) -> float {
g = feature_grid(p, cells, side);
n = len(g);
if n <= 0 { return t; }
best = g[0] ?? 0.0;
bestd = abs((g[0] ?? 0.0) - t);
for i in 1..n {
d = abs((g[i] ?? 0.0) - t);
if d < bestd { bestd = d; best = g[i] ?? 0.0; }
}
best
}
// The residual the editor must SHOW (`K-FIT`): how far the offer is from what was asked.
pub fn feature_fit_residual(p: Plan, cells: HexSet, side: integer, t: float) -> float {
abs(feature_fit_t(p, cells, side, t) - t)
}
// ── ARCS ────────────────────────────────────────────────────────────────────
//
// `X49`: an arc's CENTRE is exact but its RADIUS is not — it quantises to SHELLS, the realisable
// values of `3k^2 + m^2`. Out to 64 those are 0, 12, 36, 48. A radius between two shells draws
// the same disk as the shell BELOW it, so it round-trips as a different model with rho = 0 — the
// exact `I-QUANT` failure this doorstep exists to prevent.
// Does `n24` land on a shell — one of the realisable values of `3k^2 + m^2` — so an arc of
// that radius round-trips as itself?
pub fn arc_is_shell(n24: integer) -> boolean {
if n24 < 0 { return false; }
sh = arc_shells_upto(n24);
for i in 0..len(sh) { if (sh[i] ?? -1) == n24 { return true; } }
false
}
// Does this arc land on the grid? Answers a FIT_ code rather than a boolean, so a caller
// can tell the reader WHY it did not — see `draft_fit_reason`.
pub fn arc_fits(n24: integer) -> integer {
if arc_is_shell(n24) { return FIT_OK; }
FIT_BAD_SHELL
}
// The nearest realisable shell — `K-FIT` says *nearest*, so this can offer a LARGER disk than the
// author asked for (N = 11 is offered 12, not 0). That is deliberate and differs from what the
// unrefused value would have drawn: an off-grid N silently draws the shell BELOW it, so `arc_fill`
// would have given 0. Offering the nearest and showing the residual is the point — the author sees
// both candidates' cost — `arc_snap_n` is the other one — instead of being quietly rounded down.
// Ties go DOWN, to the smaller shell.
//
// Example: @HXI-006
pub fn arc_fit_n(n24: integer) -> integer {
if n24 <= 0 { return 0; }
sh = arc_shells_upto(n24 * 2 + 4);
best = 0;
bestd = n24;
for i in 0..len(sh) {
v = sh[i] ?? 0;
d = v - n24;
if d < 0 { d = 0 - d; }
if d < bestd { bestd = d; best = v; }
}
best
}
// How far the arc is from the nearest fitting one. 0 means it already fits; anything else
// is what a warning would quote.
pub fn arc_fit_residual(n24: integer) -> integer {
d = arc_fit_n(n24) - n24;
if d < 0 { return 0 - d; }
d
}
// THE OTHER CANDIDATE, and the reason the comment above can say *both*. This is the shell an
// UNREFUSED radius actually draws — the largest realisable one at or below `n24` — so an editor
// can show the author what the silent snap would have cost beside what the offer costs. The two
// are not close: `N = 35` is offered 36 at a residual of 1 and would have drawn **12**, an error
// of 23 in the same units, because 12 and 36 are neighbouring shells. Verified against
// `arc_fill` over every radius 0..64 rather than against the formula it shares with `arc_fits`.
//
// Example: @HXI-006
pub fn arc_snap_n(n24: integer) -> integer {
if n24 <= 0 { return 0; }
sh = arc_shells_upto(n24);
best = 0;
for i in 0..len(sh) { if (sh[i] ?? 0) > best { best = sh[i] ?? 0; } }
best
}
// How far snapping WOULD move it — signed, so a caller can see which way.
pub fn arc_snap_residual(n24: integer) -> integer { n24 - arc_snap_n(n24) }
// ── LEVELS — MEASURED TO HAVE NOTHING TO REFUSE ─────────────────────────────
//
// A level is the topological SHEET (`X58`), stored as an integer (`ha_cy` / `ck_cy`) and used as a
// FILTER BEFORE THE CUT. There is no continuous parameter here, so `I-QUANT` has nothing to say:
// nothing exists *between* two levels to be snapped to one of them. Measured over the full range
// an integer holds — the behaviour at ±10^6 is identical to the behaviour at 0.
//
// This returns `FIT_OK` unconditionally, and that is a RESULT, not an omission. @HXI-008 keeps it
// honest the only way an always-true predicate can be kept honest: by showing that the SAME
// doorstep refuses the other parameters, so "accepts every level" is not "accepts everything".
//
// Example: @HXI-008
pub fn level_fits(lv: integer) -> integer {
if lv == lv { return FIT_OK; }
FIT_OK
}
// ── MATERIAL IDS — the edge slot is one byte ─────────────────────────────────
//
// `hex_field`'s `EdgeSet.eg_mat` is a **`u8`**, and the HXF `EDGE` section is one byte per slot —
// so a wall material of 256 does not merely quantise, it becomes **0, which means NO WALL**:
// `edge_set_mat(e, .., 256)` reads back `0`. Silent deletion, not a snap, so the doorstep names
// the bound.
pub const MAT_MAX = 255;
// ⚠ NO OFFER, AND THAT IS THE POINT. `K-FIT` says a refusal offers *the nearest fitting
// alternative* — which presumes an ORDINAL parameter, where "nearest" means "almost what you
// asked for". A material id is NOMINAL: it is a name, not a magnitude. The nearest admissible id
// to 256 is 255, and 255 is a *different material* — offering it would be worse than a blank no,
// because it looks like a small correction and is not. So this refusal names its restriction and
// stops there. The author must pick a material, not accept a rounding.
//
// Example: @HXI-007
pub fn mat_fits(m: integer) -> integer {
if m <= 0 || m > MAT_MAX { return FIT_BAD_MAT; }
FIT_OK
}
// ── TERRAIN — HEIGHTS ON THE VOXEL'S GRID ───────────────────────────────────
//
// A voxel stores height as an **integer** number of units, and the world says how big one unit
// is. A height off that grid would truncate in storage, so the doorstep refuses it.
//
// `HEIGHT_SCALE` is moros's default unit (0.25 world units per height unit). It is only a
// default: a world carries its own unit (moros's `VoxelWorld.w_unit` is 0.25, 0.125 or 1.0), and
// testing a height against a grid the world does not have gives the wrong verdict. So every
// height call has an `_at` form taking the unit; the short names are those forms at
// `HEIGHT_SCALE`.
pub const HEIGHT_SCALE = 0.25;
// The voxel's own value for a height, at a given quantum: what `h_height` would hold.
pub fn height_units_at(z: float, scale: float) -> integer {
if scale <= 0.0 { return 0; }
k = z / scale ?? 0.0;
if k < 0.0 { return (k - 0.5) as integer; }
(k + 0.5) as integer
}
// Grid units back to a world height at a given scale — the inverse of `height_units_at`.
pub fn height_from_units_at(u: integer, scale: float) -> float { (u as float) * scale }
// THE DOORSTEP, at the caller's quantum. A height must land exactly on the voxel's grid; off
// it, the store truncates and `X59`'s returned residual would be wrong by the truncation —
// which is the one thing `SPEC` **G5** says must never happen silently.
//
// ⚠ **A NON-POSITIVE QUANTUM IS REFUSED BY NAME RATHER THAN DIVIDED BY.** loft's division by
// zero yields null and keeps running, so an unchecked `z / scale` would discharge to `0.0` and
// this would answer *on the grid* for `z = 0` and *off it* for everything else — a wrong verdict
// dressed as a real one, which is exactly what a doorstep exists not to produce.
pub fn seat_fits_at(z: float, scale: float) -> integer {
if scale <= 0.0 { return FIT_BAD_SCALE; }
if height_from_units_at(height_units_at(z, scale), scale) == z { return FIT_OK; }
FIT_BAD_HEIGHT
}
// The offer, and the residual the editor must show — in world units.
pub fn seat_fit_z_at(z: float, scale: float) -> float {
if scale <= 0.0 { return z; }
height_from_units_at(height_units_at(z, scale), scale)
}
// How far a height is from the nearest seating level at a given scale. Warn on this
// rather than snapping, when the author's exact value matters.
pub fn seat_fit_residual_at(z: float, scale: float) -> float { abs(seat_fit_z_at(z, scale) - z) }
// ── the same five at moros's own quantum, unchanged for every existing caller ─
// A world height in grid units at the default scale.
pub fn height_units(z: float) -> integer { height_units_at(z, HEIGHT_SCALE) }
// Grid units back to a world height at the default scale.
pub fn height_from_units(u: integer) -> float { height_from_units_at(u, HEIGHT_SCALE) }
// Example: @HXI-008
pub fn seat_fits(z: float) -> integer { seat_fits_at(z, HEIGHT_SCALE) }
// The nearest seating height at the default scale.
pub fn seat_fit_z(z: float) -> float { seat_fit_z_at(z, HEIGHT_SCALE) }
// How far off that seating height the value was; 0.0 means it already fitted.
pub fn seat_fit_residual(z: float) -> float { seat_fit_residual_at(z, HEIGHT_SCALE) }
// True when `z` lies on the voxel's grid at `HEIGHT_SCALE`.
pub fn seat_is_integral(z: float) -> boolean { seat_fits(z) == FIT_OK }
tests/01-hex-fit.loft
// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// hex_fit — the conformance tests: the doorstep refuses at authoring time what would not
// round-trip, with a named reason, an offer, and a residual.
//
// Contract only; the exhaustive measurement is hexbody's `tests/fit.loft`.
use hex_fit::*;
use hex_field::*;
fn main() {
// a valid arc shell is admitted, an off-shell radius is refused -- the doorstep DISTINGUISHES.
assert(arc_fits(36) == FIT_OK, "shell 36 is admitted");
assert(arc_fits(37) != FIT_OK, "off-shell 37 is refused");
}
fn test_a_refusal_names_its_reason() {
code = arc_fits(37);
reason = fit_reason(code);
assert(reason.len() > 0, "a refusal carries a named reason, not a blank no");
}
fn test_an_off_shell_radius_offers_the_nearest_that_fits() {
// K-FIT: a refusal offers the nearest admissible alternative. For an ORDINAL parameter (a
// radius) an offer is owed -- unlike a material id, which is nominal (X68).
n = arc_fit_n(37);
assert(arc_is_shell(n), "the offered radius is itself a shell, got {n}");
res = arc_fit_residual(37);
assert(res >= 0, "and the residual is reported, got {res}");
}
fn test_a_level_has_nothing_to_refuse_and_that_is_the_result() {
// A level is an integer index -- nothing exists between two levels to snap to -- so the
// doorstep accepts every level. A doorstep that refused more than the field distinguishes
// would make legal models unauthorable (X66).
assert(level_fits(0) == FIT_OK, "level 0 fits");
assert(level_fits(1000000) == FIT_OK, "and so does level 1e6 -- levels are identical at any n");
}
fn test_the_height_slot_is_an_integer_grid() {
// moros's h_height is an integer at HEIGHT_SCALE wu (X67). A height off that grid truncates
// silently, so the doorstep refuses it -- and an on-grid height round-trips.
u = height_units(height_from_units(3));
assert(u == 3, "an on-grid height round-trips through the voxel, got {u}");
}
fn test_the_height_quantum_is_the_callers() {
// ⛔ THE CONSTANT CANNOT SERVE A DOORSTEP. A world carries its own height unit, and a
// consumer measured this package giving the WRONG VERDICT on 40 of 81 sampled heights at
// 0.125 and 30 of 81 at 1.0 (moros probe/b7). X66 says a doorstep wrong about the grid is
// worse than none -- the case it did not cover is testing against a grid the field lacks.
//
// A HALF-UNIT AT ONE QUANTUM IS A WHOLE UNIT AT ANOTHER, which is the whole of it.
assert(seat_fits_at(0.125, 0.25) == FIT_BAD_HEIGHT, "0.125 is half a unit at 0.25");
assert(seat_fits_at(0.125, 0.125) == FIT_OK, "...and a whole one at 0.125");
assert(seat_fits_at(0.5, 1.0) == FIT_BAD_HEIGHT, "0.5 is half a unit at 1.0");
assert(seat_fits_at(2.0, 1.0) == FIT_OK, "...and 2.0 is two whole ones");
// The offer and the residual follow the quantum they were given.
assert(seat_fit_z_at(0.3, 0.125) == 0.25, "the offer at 0.125 is 0.25, got {seat_fit_z_at(0.3, 0.125)}");
assert(seat_fit_z_at(0.3, 1.0) == 0.0, "...and at 1.0 it is 0.0, got {seat_fit_z_at(0.3, 1.0)}");
assert(abs(seat_fit_residual_at(0.3, 1.0) - 0.3) < 1e-12, "with the residual to match");
// The default is unchanged for every existing caller -- the wrappers ARE the old behaviour.
assert(seat_fits(0.3) == seat_fits_at(0.3, HEIGHT_SCALE), "the wrapper is the _at form at 0.25");
assert(seat_fit_z(0.3) == seat_fit_z_at(0.3, HEIGHT_SCALE), "and so is the offer");
assert(height_units(0.75) == height_units_at(0.75, HEIGHT_SCALE), "and the unit count");
}
fn test_a_quantum_that_is_not_a_grid_is_refused_by_name() {
// ⚠ loft's division by zero yields null and keeps running, so an unchecked `z / scale`
// discharges to 0.0 -- and the doorstep would answer "on the grid" for 0 and "off it" for
// everything else. A wrong verdict dressed as a real one is what a doorstep exists not to
// produce, so a non-positive quantum has its own code.
assert(seat_fits_at(1.0, 0.0) == FIT_BAD_SCALE, "a zero quantum is not a grid");
assert(seat_fits_at(0.0, 0.0) == FIT_BAD_SCALE, "...and zero height does not excuse it");
assert(seat_fits_at(1.0, 0.0 - 0.25) == FIT_BAD_SCALE, "nor does a negative one");
assert(fit_reason(FIT_BAD_SCALE) != "", "and the refusal carries a reason");
assert(fit_reason(FIT_BAD_SCALE) != fit_reason(FIT_BAD_HEIGHT),
"...which is not the height reason wearing a different code");
}
tests/02-worked-examples.loft
// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Worked examples for `hex_fit` (@PLN141). Each `test_*` below is a REAL,
// run-every-CI demonstration of a contract the signature cannot state; the
// function documents itself by CITING the tag with `// Example: @HXI-00N`, and
// `check_doc_drift.sh examples` fails if a citation goes dangling. These are
// teaching examples, distinct from the conformance suite in `01-hex-fit.loft`.
//
// WHAT THIS PACKAGE IS FOR. Everything a caller authors here is CONTINUOUS —
// a radius, a position along a wall, a height, a direction — and the field it
// is stored in is not. An off-grid value is therefore SNAPPED, silently, and
// the round trip then reports success on a model nobody wrote. `hex_fit` is
// the doorstep that refuses such a value at authoring time, with a NAMED
// reason, an OFFER, and the RESIDUAL to show the author.
//
// Two things follow that a signature cannot carry, and both are worked below:
//
// a refusal is DATA, not an error every `fits` map returns an integer code,
// never a bare false and never a raise
// the grid is READ OFF THE FIELD never computed from a formula — the field
// owns the answer, and a private copy of it
// has already been wrong once (see @HXI-005)
use hex_fit::*;
use hex_field::*;
use hex_form::*;
use hex_draw::*;
use hex_grid::*;
use hex_shape::*;
use hex_recover::*;
const W = 41;
const H = 41;
const Q0 = 0 - 20;
const R0 = 0 - 20;
const WALLMAT = 1; // the stencil's own outline
const EMBED = 5; // the embedded run — a DIFFERENT material, on interior edges only
// The stencil every draft example is written on: a regular hexagon, six sides of six steps.
// It is written the way `form_write` writes one, so it is already its own canonical text.
fn hexagon() -> text {
"stencil h4 h0 0\nside 0 len 6 turn 2\nside 1 len 6 turn 2\nside 2 len 6 turn 2"
+ "\nside 3 len 6 turn 2\nside 4 len 6 turn 2\nside 5 len 6 turn 2"
}
// A patch of lattice big enough to hold the hexagon well clear of its edges.
fn patch() -> HexSet { hexset_chunk(Q0, R0, W, H) }
// How many edges the two fields disagree about — 0 means they are the same drawing.
fn edge_diff(ea: EdgeSet, eb: EdgeSet) -> integer {
n = 0;
for r in R0..R0 + H {
for q in Q0..Q0 + W {
for d in 0..3 {
(nq, nr) = hex_neighbor(q, r, d);
if edge_mat(ea, q, r, nq, nr) != edge_mat(eb, q, r, nq, nr) { n = n + 1; }
}
}
}
n
}
// Is `t` one of the admissible positions? A membership test, because `feature_grid` returns
// them in the order the field stores them and NOT in `t` order.
fn grid_has(g: vector<float>, t: float) -> boolean {
for i in 0..len(g) { if abs((g[i] ?? -1.0) - t) < 1e-12 { return true; } }
false
}
// The smallest call that does something real: author a stencil, ask the doorstep, draw it,
// and check the drawing spells the same stencil back.
fn main() {
f = form_read(hexagon());
cells = patch();
form_fill(f, 0, 0, cells);
s = draft_new(f, 3, 0 - 6, 9, 1); // one period of direction 3
assert(draft_fits(s, cells) == FIT_OK, "the doorstep admits it: {fit_reason(draft_fits(s, cells))}");
edges = edgeset_new(Q0, R0, W, H);
draft_draw(s, 0, 0, cells, edges, WALLMAT, EMBED);
assert(draft_rebuild_text(cells, edges, "h4", EMBED) == draft_canon_text(s, cells, "h4"),
"what was drawn spells the stencil back");
}
// @HXI-001 — ONE MODEL, ONE TEXT, and a run has two spellings. The whole format
// exists so that a `diff` means *the model changed*: a project file is diffable
// and an undo history is exact only because one model has exactly one text.
//
// A run breaks that on its own. A wall from A to B and the same wall from B to A
// mark the IDENTICAL edges — the field stores no orientation — so `draft_write`
// can spell one wall two ways, and only one of the two comes back out of the
// field. Measured over all 24 directions: the two spellings differ in ZERO
// edges, and exactly one of them survives the round trip, never both.
//
// So the text to diff against is `draft_canon_text`, never `draft_write`. The
// canonical spelling is READ OFF THE FIELD — it is what the reader answers —
// because any other rule could drift from the reader and this one cannot.
fn test_a_run_has_two_spellings_and_the_field_answers_one() {
f = form_read(hexagon());
cells = patch();
form_fill(f, 0, 0, cells);
// The same wall, named from each of its two ends. One period of direction 3
// from (-6,9) ends at (9,15); direction 15 is direction 3 reversed.
fwd = draft_new(f, 3, 0 - 6, 9, 1);
rev = draft_new(f, 15, 9, 15, 1);
assert(draft_fits(fwd, cells) == FIT_OK, "the forward spelling is a legal wall");
assert(draft_fits(rev, cells) == FIT_OK, "and so is the reverse — the doorstep admits both");
c1 = patch();
e1 = edgeset_new(Q0, R0, W, H);
n1 = draft_draw(fwd, 0, 0, c1, e1, WALLMAT, EMBED);
c2 = patch();
e2 = edgeset_new(Q0, R0, W, H);
n2 = draft_draw(rev, 0, 0, c2, e2, WALLMAT, EMBED);
assert(n1 == 8 && n2 == 8, "each marks the same 8 interior edges, got {n1} and {n2}");
assert(edge_diff(e1, e2) == 0, "and the two fields are the same drawing, edge for edge");
// The field answers with ONE of the two spellings, and it is the forward one here.
want = hexagon() + "\nwall d 3 a -6 b 9 p 1";
assert(draft_rebuild_text(c1, e1, "h4", EMBED) == want, "the field spells the wall this way");
assert(draft_rebuild_text(c2, e2, "h4", EMBED) == want, "from either authoring — one field, one text");
// …so the naive round trip FAILS on the reverse spelling, with nothing wrong.
assert(draft_write(fwd, "h4") == want, "the forward author's own text matches");
assert(draft_write(rev, "h4") != want,
"the reverse author's does not — and the model is byte-identical in the field");
// The canon is what makes the diff mean something again.
assert(draft_canon_text(fwd, cells, "h4") == want, "canon of the forward spelling");
assert(draft_canon_text(rev, cells, "h4") == want, "canon of the reverse spelling — the same text");
// A stencil with no run at all canonicalises to the form alone.
none = draft_new(f, 0 - 1, 0, 0, 0);
assert(draft_canon_text(none, cells, "h4") == hexagon(), "no run, no wall line");
}
// @HXI-002 — THE ROUND TRIP HAS TO NOTICE A DROPPED RUN. A stencil carries its
// linework as material on INTERIOR edges — edges whose two cells are both in the
// footprint — so the footprint and the form recovery read a slot the run never
// writes. That orthogonality is what makes the run cheap to add, and it is also
// what makes losing it invisible: the cells are all still there, the form still
// recovers, and a trip that compares only the form reports SUCCESS on a model
// missing its content.
//
// `draft_rebuild_text` reads the run back as well, so a dropped run is a text
// difference — loud, and in the same diff as everything else.
fn test_the_round_trip_notices_a_dropped_run() {
f = form_read(hexagon());
cells = patch();
form_fill(f, 0, 0, cells);
with_run = draft_new(f, 3, 0 - 6, 9, 1);
want = hexagon() + "\nwall d 3 a -6 b 9 p 1";
c1 = patch();
e1 = edgeset_new(Q0, R0, W, H);
n1 = draft_draw(with_run, 0, 0, c1, e1, WALLMAT, EMBED);
assert(n1 == 8, "the run marks 8 interior edges, got {n1}");
assert(draft_rebuild_text(c1, e1, "h4", EMBED) == want, "and the trip returns the whole stencil");
// The SAME stencil drawn without its run: the footprint is identical, and the text is not.
no_run = draft_new(f, 0 - 1, 0, 0, 0);
c2 = patch();
e2 = edgeset_new(Q0, R0, W, H);
n2 = draft_draw(no_run, 0, 0, c2, e2, WALLMAT, EMBED);
assert(n2 == 0, "nothing embedded, got {n2}");
assert(rebuild_construct_text(c2, "h4") == rebuild_construct_text(c1, "h4"),
"the FORM recovery cannot tell the two apart — that is the hole");
assert(draft_rebuild_text(c2, e2, "h4", EMBED) == hexagon(), "the run half can: no wall line");
assert(draft_rebuild_text(c2, e2, "h4", EMBED) != want, "so dropping the run FAILS the trip");
}
// @HXI-003 — THE READER ACCEPTS EXACTLY WHAT THE WRITER EMITS, and a refusal is
// a stencil with an EMPTY FORM, not an error: nothing in `-> Draft` says the
// parse can fail, so the check is yours to write and it is
// `form_sides(s.df_form) == 0`.
//
// Strictness here is not fussiness. The text IS the contract, so a text this
// library cannot write is a text it will not read — otherwise a `diff` could be
// defeated by a trailing space. Every refusal below was ADMITTED and silently
// re-written before this was measured, and one of them was not a spelling at
// all: `wall d x …` parsed as `d 0`, which is not a missing wall but a wall
// pointing SOMEWHERE ELSE, admitted into the model without a word.
fn test_the_reader_takes_exactly_what_the_writer_emits() {
good = hexagon() + "\nwall d 3 a -6 b 9 p 1";
g = draft_read(good);
assert(form_sides(g.df_form) == 6, "the canonical text reads six sides");
assert(draft_has_run(g) && g.df_d == 3 && g.df_a == 0 - 6 && g.df_p == 1, "and the run it names");
assert(draft_write(g, "h4") == good, "and it re-writes byte for byte");
// A trailing space is invisible in an editor and is not the layout.
assert(form_sides(draft_read(good + " ").df_form) == 0, "a trailing space is refused");
// So is an extra field, however harmless it looks.
assert(form_sides(draft_read(good + " note").df_form) == 0, "an extra field is refused");
// The sharp one: a field that is not a number. Repaired to 0 it is a wall in
// direction 0 — a different wall, in a different place, drawn without comment.
assert(form_sides(draft_read(hexagon() + "\nwall d x a -6 b 9 p 1").df_form) == 0,
"a non-integer direction is refused, not read as direction 0");
// A double space is the same defect wearing different clothes: field 2 is empty.
assert(form_sides(draft_read(hexagon() + "\nwall d a -6 b 9 p 1").df_form) == 0,
"an empty direction is refused");
// One integer, one spelling — `+3` and `003` name the same direction as `3`,
// and two texts for one model is exactly what the format forbids.
assert(form_sides(draft_read(hexagon() + "\nwall d +3 a -6 b 9 p 1").df_form) == 0, "`+3` is refused");
assert(form_sides(draft_read(hexagon() + "\nwall d 003 a -6 b 9 p 1").df_form) == 0, "`003` is refused");
// The writer appends the wall line LAST, so a wall line between two `side`
// lines is a line order it cannot emit — and re-writing it would move it.
middle = "stencil h4 h0 0\nside 0 len 6 turn 2\nside 1 len 6 turn 2\nwall d 3 a -6 b 9 p 1"
+ "\nside 2 len 6 turn 2\nside 3 len 6 turn 2\nside 4 len 6 turn 2\nside 5 len 6 turn 2";
assert(form_sides(draft_read(middle).df_form) == 0, "a wall line in the middle is refused");
// The scope is ONE run per stencil, so two records are refused rather than one of them chosen.
assert(form_sides(draft_read(good + "\nwall d 5 a 0 b 0 p 1").df_form) == 0, "two wall lines are refused");
assert(form_sides(draft_read(good + "\n").df_form) == 0, "and a trailing newline is refused");
// …and the good text still reads, which is what makes the eight refusals above
// a narrowing rather than a wall.
assert(form_sides(draft_read(good).df_form) == 6, "the writer's own text still reads");
assert(form_sides(draft_read(hexagon()).df_form) == 6, "a stencil with no run reads too");
}
// @HXI-004 — SPELLING IS THE READER'S JOB; LEGALITY IS THE DOORSTEP'S. These are
// two different gates and it is worth knowing which one you are asking.
// `draft_read` answers *is this the text we write?* and nothing more: it admits
// `wall d 99 … p -1`, because 99 and -1 are spelled exactly as this library
// spells an integer. `draft_fits` answers *is this a wall the field can hold?*
// and refuses both, each with its own reason.
//
// And a refusal owes an OFFER: `draft_fit_p` returns the longest run from the
// same anchor in the same direction that does fit, searching DOWNWARD so the
// offer is never longer than what was asked for. 0 is a usable answer too — it
// means move the anchor.
fn test_spelling_is_one_gate_and_legality_is_another() {
f = form_read(hexagon());
cells = patch();
form_fill(f, 0, 0, cells);
// Spelled correctly, and not a wall the field can hold.
wide = draft_read(hexagon() + "\nwall d 99 a -6 b 9 p 1");
assert(form_sides(wide.df_form) == 6 && wide.df_d == 99, "the reader admits direction 99");
assert(draft_fits(wide, cells) == FIT_BAD_DIR, "the doorstep refuses it");
back = draft_read(hexagon() + "\nwall d 3 a -6 b 9 p -1");
assert(form_sides(back.df_form) == 6 && back.df_p == 0 - 1, "the reader admits -1 periods");
assert(draft_fits(back, cells) == FIT_BAD_P, "the doorstep refuses that too");
// Each refusal NAMES its restriction, and the names are different — a doorstep
// whose every no is the same no tells the author nothing.
centre = draft_new(f, 3, 0, 0, 1); // (0,0) is a hex CENTRE
assert(draft_fits(centre, cells) == FIT_NO_VERTEX, "an anchor on a centre is refused");
assert(fit_reason(FIT_NO_VERTEX) != fit_reason(FIT_OUTSIDE), "and the reasons are distinct");
assert(fit_reason(draft_fits(centre, cells)).len() > 0, "a refusal is never a blank no");
// The offer. A 9-period run leaves the hexagon; 3 periods is the longest that stays inside.
long_run = draft_new(f, 3, 0 - 6, 9, 9);
assert(draft_fits(long_run, cells) == FIT_OUTSIDE, "a run that pokes out is refused");
offer = draft_fit_p(long_run, cells);
assert(offer == 3, "the offer is the longest that fits, got {offer}");
assert(draft_fits(draft_new(f, 3, 0 - 6, 9, offer), cells) == FIT_OK, "and the offer itself fits");
assert(draft_fits(draft_new(f, 3, 0 - 6, 9, offer + 1), cells) == FIT_OUTSIDE,
"one period longer does not — so the offer is the boundary, not a guess");
}
// @HXI-005 — THE ADMISSIBLE POSITION IS READ OFF THE FIELD, NEVER COMPUTED. A
// feature is authored as `(side, t)` with `t` running 0..1 along the side; the
// field stores a material on an EDGE. So the only `t` that survives is one an
// edge centre already sits at, and the doorstep's whole job is knowing which.
//
// The design note that recorded those centres as `(2i+1)/2n` was measured and is
// true for ONE of the two side families. A hexagonal lattice gives a rectangle
// two kinds of side: one whose edges tile it, and one whose edges are INSET,
// spanning exactly `sqrt(3)/2` of the side. A formula fitted to the first
// refuses every legal position on the second — which is why `feature_grid`
// consults `side_edges` and cannot make that mistake.
//
// The cost of the difference is not academic: the middle of the side, `t = 0.5`,
// is the most natural place to put a door, and it is legal on one family and
// refused on the other.
fn test_the_admissible_position_is_read_off_the_field() {
p = plan_new(0, 0, 5, 4, 0, false);
cells = patch();
lb = labels_new(Q0, R0, W, H);
draw_floor(p, cells, lb, 9);
g0 = feature_grid(p, cells, 0); // a -u/+u face: the INSET family
g1 = feature_grid(p, cells, 1); // a -v/+v face: the TILING family
assert(len(g0) == 9, "side 0 carries 9 edges, got {len(g0)}");
assert(len(g1) == 10, "side 1 carries 10, got {len(g1)}");
// The tiling family IS the formula: every `(2i+1)/2n` is a position on it.
for i in 0..len(g1) {
t = ((2.0 * (i as float) + 1.0) / (2.0 * (len(g1) as float))) ?? 0.0;
assert(grid_has(g1, t), "side 1 carries the formula position {t}");
}
// The inset family is not, and the number that says so is its SPAN.
lo = 2.0;
hi = 0.0 - 1.0;
for i in 0..len(g0) {
v = g0[i] ?? 0.0;
if v < lo { lo = v; }
if v > hi { hi = v; }
}
assert(abs((hi - lo) - 0.8660254037844386) < 1e-12,
"the inset family spans exactly sqrt(3)/2 of its side, got {hi - lo}");
assert(!grid_has(g0, (1.0 / 18.0) ?? 0.0), "so the formula's first position is not on it");
// The middle of the side: legal on the odd-count family, refused on the even one.
assert(feature_fits(p, cells, 0, 0.5, 1) == FIT_OK, "a door in the middle of side 0 fits");
assert(feature_fits(p, cells, 1, 0.5, 1) == FIT_BAD_T, "the same door on side 1 does not");
assert(abs(feature_fit_t(p, cells, 1, 0.5) - 0.45) < 1e-12,
"and the offer is the nearest edge centre, got {feature_fit_t(p, cells, 1, 0.5)}");
assert(abs(feature_fit_residual(p, cells, 1, 0.5) - 0.05) < 1e-12,
"with the residual the author has to be shown");
assert(feature_fit_residual(p, cells, 0, 0.5) == 0.0, "on side 0 there is nothing to move");
// A span, and a side that is not a side, are refused by NAME rather than by false.
assert(feature_fits(p, cells, 0, 0.5, 0) == FIT_BAD_SPAN, "a feature covering no edge");
assert(feature_fits(p, cells, 0, 0.5, 99) == FIT_BAD_SPAN, "or more than the side has");
assert(feature_fits(p, cells, 9, 0.5, 1) == FIT_NO_SIDE, "side 9 is not a side");
}
// @HXI-006 — AN OFF-GRID RADIUS IS REFUSED, AND THE TWO CANDIDATES COST DIFFERENT
// AMOUNTS. An arc's CENTRE is exact and its RADIUS is not: only the shells
// `3k^2 + m^2` exist, which out to 64 are 0, 12, 36 and 48. Ask for anything
// else and `arc_fill` draws the shell BELOW it, so the model round-trips as a
// different model with a residual of zero — the silent snap this doorstep is for.
//
// The refusal offers the NEAREST shell, which can be LARGER than what was asked
// for, and that is deliberate: it is a different candidate from the one an
// unrefused call would have drawn, and the author should see both prices.
// `N = 35` is the case that makes it concrete — the offer is one unit away and
// the silent snap is twenty-three, because 12 and 36 are neighbouring shells.
fn test_an_off_shell_radius_shows_both_candidates() {
assert(arc_fits(36) == FIT_OK, "36 is a shell");
assert(arc_fits(35) == FIT_BAD_SHELL, "35 is not");
assert(arc_fit_n(35) == 36, "the offer is the nearest shell, got {arc_fit_n(35)}");
assert(arc_fit_residual(35) == 1, "one unit away");
assert(arc_snap_n(35) == 12, "what an unrefused 35 draws, got {arc_snap_n(35)}");
assert(arc_snap_residual(35) == 23, "twenty-three units away — the price nobody quotes");
// …and that is measured against the drawing, not against the shell formula.
disk = patch();
arc_fill(0, 0, 35, disk);
assert(arc_shell_max(disk, 0, 0) == arc_snap_n(35), "the field agrees with arc_snap_n");
assert(arc_shell_max(disk, 0, 0) != 35, "and the 35 is stored nowhere");
// Ties go DOWN, so an offer is never a promotion the author did not ask for.
assert(arc_fit_n(24) == 12, "24 is equidistant from 12 and 36; the offer is 12");
sh = arc_shells_upto(64);
assert(len(sh) == 4, "four shells exist out to 64, got {len(sh)}");
assert((sh[0] ?? -1) == 0 && (sh[1] ?? -1) == 12 && (sh[2] ?? -1) == 36 && (sh[3] ?? -1) == 48,
"and they are 0, 12, 36, 48");
}
// @HXI-007 — A MATERIAL ID IS NOMINAL, SO ITS REFUSAL OFFERS NOTHING. Every other
// refusal in this package offers the nearest fitting alternative, because every
// other parameter is ORDINAL — "nearest" means "almost what you asked for". A
// material id is a NAME. The nearest admissible id to 256 is 255, and 255 is a
// different material: offering it would look like a small correction and would
// not be one. So this refusal names its restriction and stops there.
//
// What it is protecting you from is not a rounding either. The wall slot is one
// byte, and the store's narrowing cast is CHECKED: a value it cannot hold does not
// truncate to its low byte. It used to become 0 — and 0 means NO WALL, so an
// out-of-range id DELETED the wall it was setting, before storage was involved at
// all. `hex_field` 0.1.1 refuses that write instead (@PLN141 row 10), so the slot
// now keeps what it held and `edgeset_refused` counts the attempt. The doorstep
// below is not thereby redundant: it names the restriction where the author can
// still act on it, and it refuses 0, which the SLOT accepts as the absence of a
// wall rather than as a material.
fn test_a_material_id_is_a_name_not_a_magnitude() {
assert(mat_fits(1) == FIT_OK, "1 is a material");
assert(mat_fits(MAT_MAX) == FIT_OK, "and so is the largest the slot holds");
assert(mat_fits(MAT_MAX + 1) == FIT_BAD_MAT, "one more is refused");
assert(mat_fits(0) == FIT_BAD_MAT, "0 is not a material — it is the absence of one");
assert(mat_fits(0 - 1) == FIT_BAD_MAT, "nor is a negative id");
// The measurement behind the refusal, on a real edge.
eg = edgeset_new(Q0, R0, W, H);
(nq, nr) = hex_neighbor(0, 0, 0);
edge_set_mat(eg, 0, 0, nq, nr, MAT_MAX);
assert(edge_mat(eg, 0, 0, nq, nr) == MAT_MAX, "255 is stored as 255");
edge_set_mat(eg, 0, 0, nq, nr, MAT_MAX + 1);
assert(edge_mat(eg, 0, 0, nq, nr) == MAT_MAX,
"256 is refused: the wall it would once have erased still reads 255");
edge_set_mat(eg, 0, 0, nq, nr, MAT_MAX + 2);
assert(edge_mat(eg, 0, 0, nq, nr) == MAT_MAX, "and 257 likewise — no low-byte truncation");
assert(edgeset_refused(eg) == 2, "and both refusals are counted rather than silent");
}
// @HXI-008 — A DOORSTEP WITH NOTHING TO REFUSE SAYS SO, and that is a result
// rather than an omission. A level is the topological SHEET a plan sits on,
// stored as an integer and used as a filter before the cut. Nothing exists
// BETWEEN two levels, so there is nothing to snap and nothing to refuse: a
// doorstep that invented a restriction here would make legal models unauthorable,
// which is worse than having none.
//
// An always-true predicate can only be kept honest one way — by showing the SAME
// doorstep refusing something — so a height is measured beside it. A height DOES
// have a grid, an invisible one: the voxel stores it as a whole number of
// quarter-units, so 0.3 is not a height, it is 0.25 with the difference thrown
// away where nobody would look for it.
fn test_a_level_has_nothing_to_refuse_and_a_height_does() {
assert(level_fits(0) == FIT_OK, "level 0 fits");
assert(level_fits(1000000) == FIT_OK, "so does level 1e6");
assert(level_fits(0 - 1000000) == FIT_OK, "and so does level -1e6 — every level, at any n");
// The same doorstep, on the parameter that does have a grid.
assert(seat_fits(0.75) == FIT_OK, "0.75 is three whole units");
assert(height_units(0.75) == 3, "which is what the voxel stores");
assert(height_from_units(3) == 0.75, "and it reads back exactly");
assert(seat_fits(0.3) == FIT_BAD_HEIGHT, "0.3 is not on the grid");
assert(seat_fit_z(0.3) == 0.25, "the offer is the height the voxel would have kept");
assert(abs(seat_fit_residual(0.3) - 0.05) < 1e-12, "and the residual is what it would have thrown away");
assert(seat_fits(0.0 - 0.3) == FIT_BAD_HEIGHT, "below the datum is the same story");
assert(seat_fit_z(0.0 - 0.3) == 0.0 - 0.25, "with the offer rounded the same way");
assert(seat_is_integral(1.0) && !seat_is_integral(1.1), "the predicate form of the same test");
}