hex_body source

← hex_body · API reference · version 0.3.6

4 file(s), 1472 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.

ItemDefinedWorked exampleCall sites
Joint struct28—36 37 86 94 101 233
joint_new fn36—234 44
pose_of fn46HXB-001448 456 14 20 21 22 … (7 total)
wheel_value fn59HXB-00733 38 304 306 315
wheel_angle fn65—80 317
wheel_skid fn79HXB-00733 38 304 307 315
joint_fits fn86HXB-00245 46 48 111 119
joint_offer fn94HXB-002102 47 48 117 119
joint_residual fn101—118
Rig struct163—178 179 189 190 203 204 … (25 total)
rig_new fn178—347 349 350 351 352 354 … (50 total)
rig_bone3 fn189HXB-004205 379 52 60 67 68 … (29 total)
rig_bone fn203HXB-001369 53 54 69 70 25 … (16 total)
rig_count fn209—62 104 106 238 84 347 … (10 total)
bone_planar fn214—225 281 261
rig_planar fn223HXB-005263 264 185 186 207
rig_joint fn233HXB-00286 110
rig_admissible fn242HXB-00859 73 209 214 215 239 … (11 total)
rig_write fn277HXB-00855 58 41 55 61 62 … (19 total)
rig_read fn344HXB-00856 62 70 104 106 342 … (12 total)
rig_eq fn392—57 71 86 88 89 90 … (9 total)
rig_world_seg fn436HXB-005602 643 74 126 135 145 … (19 total)
Frame struct485—496 544 553
rig_world_frame3 fn496HXB-003565 578 281 303 135
frame_point fn553HXB-003565 579 580 282 283 292 … (9 total)
rig_world_point3 fn563HXB-003291 342 348 143
rig_world_seg3 fn576HXB-003127 136 146 156 165 176 … (18 total)
Obb struct585—601 609 614 91
bone_obb fn601HXB-00673 231 255
obb_contains fn614—83 90 93 234 268
seg_dist fn623—644 81 267
bone_shape_has fn641HXB-006281 282

src/hex_body.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// hex_body — HEXBODY'S OWN BODY MODEL: rigs, revolute joints with computed limits, pure poses, proxies.
//
// `G1`, the first brick of the body half (`plans/m1-moving-body/`).  The load-bearing decision,
// from the user: **a body is a RIG — bones and the limits in the joints between them, never a
// pose** (`SPEC` **I-POSE**).  So this module never stores a pose; it COMPUTES one from the current
// joint values, and that computation is a **pure function** (`SPEC` **I6**).  Purity is not a
// testability nicety here — it IS the scope boundary `L15` draws: hexbody answers *where a part is
// NOW*, never *what it will do*, and a pose that depended on history would have smuggled in a
// future.
//
// Re-measured from crawler's `hexwheel`/`wheeltest` (`T2 → T1`): the wheel's rolling identity holds
// **by construction, not by tuning**, because the joint value is computed FROM travel rather than
// accumulated.  Its own framing — *"phase is computed from the distance, so `Δφ·2πr = Δd` holds by
// construction"* — is exactly `I6`.  ⚠ **Flex is joints, not deformation** (`I-POSE`): a part that
// bends is more bones on more joints, so this survives a flexing wing unchanged and hexbody needs
// no skinning.

const TAU = 6.283185307179586;   // one turn, in radians

fn fabs(x: float) -> float { if x < 0.0 { 0.0 - x } else { x } }

// A REVOLUTE JOINT: its current value (in TURNS — a value of 1.0 is one revolution, the number a
// contact point actually cares about) and its LIMITS.  The limits are the joint's `fits?` doorstep
// (`K-FIT`): a joint value is ORDINAL, so a refusal owes an OFFER of the nearest admissible — unlike
// a material id, which is nominal and offers nothing (`X68`).
pub struct Joint {
  pub jt_value: float,   // the joint value, in turns
  pub jt_lo:    float,   // limit low  (turns)
  pub jt_hi:    float,   // limit high (turns)
}

// One articulation: its current value and the range it may move in.  The limits travel
// WITH the joint so a pose cannot be built that the rig would refuse.
pub fn joint_new(value: float, lo: float, hi: float) -> Joint {
  Joint { jt_value: value, jt_lo: lo, jt_hi: hi }
}

// FORWARD KINEMATICS (phase B): a point `(lx, ly)` in a part's LOCAL frame, posed by rotating it
// through the joint's angle.  A PURE FUNCTION of `(value, lx, ly)` — it reads no stored state, no
// elapsed time, no call count, and no previous value.  Two different paths to the same `value`
// therefore give byte-identical output, which is `I6` and the precondition for `I9`'s replay.
//
// Example: @HXB-001
pub fn pose_of(value: float, lx: float, ly: float) -> (float, float) {
  th = value * TAU;
  c = cos(th);
  s = sin(th);
  (lx * c - ly * s, lx * s + ly * c)
}

// A WHEEL is a revolute joint whose value is DERIVED from how far it has travelled (phase C):
// `value = travel / (2πr)`.  Because the value is a function of travel and not an accumulation of
// steps, the no-slip identity is exact by construction.  `slip` is the deliberate defect knob —
// 0 for a true roll; non-zero is a skid, and it is the control that keeps `wheel_skid` honest.
//
// Example: @HXB-007
pub fn wheel_value(travel: float, radius: float, slip: float) -> float {
  if radius < 0.000000001 { return 0.0; }
  (1.0 + slip) * (travel / (TAU * radius) ?? 0.0)
}

// The joint value as an ANGLE in radians.
pub fn wheel_angle(value: float) -> float { value * TAU }

// THE SKID — the contact point's slip, `|r·θ − travel|`.  For a value derived by `wheel_value`
// with `slip = 0` this is **machine-ε, not algebraic zero** — and that distinction was measured,
// not assumed: `travel → value → r·θ` is a float round-trip (a divide by `2πr` then a multiply by
// it), which f64 does not close bit-for-bit, so the residual is ~1 ulp of `travel`.  This is the
// BODY'S SOLE FLOAT STEP — the wheel's `ε_seam` (`SPEC` **I-FSEAM**, `X53`): the pose/roll
// computation is exactly where imprecision is licensed, bounded by machine ε and nowhere else.
// It is NOT a `P4` violation: `P4` governs the R1 geometry round trip, which is integer and exact;
// a wheel's roll is kinematics, which is the float frame.  Drive `θ` independently of `travel` —
// the spec's *"spin a wheel off-travel"* — and the skid is a real distance, orders of magnitude
// above ε.
//
// Example: @HXB-007
pub fn wheel_skid(radius: float, value: float, travel: float) -> float {
  fabs(radius * wheel_angle(value) - travel)
}

// THE DOORSTEP (`K-FIT` for a joint): a value inside the joint's limits fits.
//
// Example: @HXB-002
pub fn joint_fits(j: Joint, value: float) -> boolean {
  value >= j.jt_lo && value <= j.jt_hi
}

// The OFFER a refusal owes — the nearest admissible value.  A joint value is ordinal, so clamping
// to the limit is a real correction the editor can show, not a silent snap to a different thing.
//
// Example: @HXB-002
pub fn joint_offer(j: Joint, value: float) -> float {
  if value < j.jt_lo { return j.jt_lo; }
  if value > j.jt_hi { return j.jt_hi; }
  value
}

// The residual an offer carries — how far the requested value was from admissible.
pub fn joint_residual(j: Joint, value: float) -> float {
  fabs(value - joint_offer(j, value))
}

// ── THE RIG, AND ITS CANONICAL TEXT (phase A of plans/m1-moving-body) ────────
//
// A body's `original` is a RIG — `⟨bones, joint limits⟩`, never a pose (`SPEC` **I-POSE**).  A
// bone is a segment with a **joint at its base** connecting it to its PARENT bone; the root bone
// has no parent.  This is the shape a **flexing wing** takes: not a soft bone but more bones on
// more joints, so the rig is a tree and the flex is joint values along it (`I-POSE`).
//
// The rig is a DESCRIPTION, so it round-trips like a stencil's `Form`: `write(read(R)) = R`
// **byte-for-byte**, and a malformed text is REFUSED rather than repaired.  A rig carries floats
// (offsets, lengths, joint limits), and the float↔text round trip is byte-exact in loft, so the
// text is exact with no tolerance (`I-EXACT`/`P4`: content we authored recovers exactly).
//
// INDEX ORDER IS THE CANONICAL LABELLING.  A tree can be relabelled and describe the same rig; the
// canonical form fixes the labels by requiring **topological order** — the root is bone 0 and every
// bone's parent is an EARLIER bone.  The PARSER enforces the labelling (a bone out of order is
// refused), the same way `form_read` fixes the start corner by index order (`C2`); the PARENT is
// `rig_admissible`'s, because a forward or absent parent round-trips faithfully and so is not a
// spelling question.  Reading a rig is not the same as being allowed to pose it.  Fuller canonicalisation — collapsing every relabelling — is the census-shaped
// problem `S5`–`S8` solved for forms; phase A's contract is the byte round trip, which topological
// order plus a strict parser delivers.

// ── THE THIRD DIMENSION (`G1b`) — an explicit axis, and an `oz` ──────────────
//
// A revolute joint in a PLANE needs no axis: there is only one, `+z`, and the
// 2-D code leaves it implicit by ADDING joint angles.  In space it has to be
// said, and `hex_part::Hinge` already says it with the same eight numbers —
// `{ox, oy, oz, ax, ay, az, lo, hi}`, of which the planar rig's are the six
// minus two.  So this extension unifies two libraries that already agree on
// units (TURNS) and on convention, rather than inventing a third shape.
//
// ⚠ **THE AXIS IS STORED AS GIVEN AND IS NOT NORMALISED**, which is
// `hex_part/hinge.loft`'s rule kept verbatim: *"the length of the axis carries
// no meaning; a consumer that needs a unit vector normalises where it needs
// one"*.  `rig_world_seg3` is that consumer and normalises at use.
//
// ⚠⚠ **A PLANAR RIG IS BIT-IDENTICAL, AND ONLY A SINGLE BONE IS** — measured
// before this was written, because it is the claim the whole extension rests
// on.  `rig_world_seg` composes by ADDING ANGLES (`th = pth + v`, then one
// `cos`/`sin`); any 3-D form composes ROTATIONS, and `cos(a+b)` is not
// `cos a cos b - sin a sin b` bit-for-bit in f64.  Over a five-bone planar
// chain at awkward angles:
//
//     quaternion form   0 of 5 bones identical, worst 6.7e-16 m
//     matrix form       the ROOT identical,     worst 2.2e-16 m
//
// so the matrix is what shipped.  ⚠ The quaternion's root fails for a reason
// worth keeping: it stores `cos(theta/2)` and rebuilds the rotation through
// double-angle arithmetic, where Rodrigues at `(0,0,1)` collapses to exactly
// `[[c,-s,0],[s,c,0],[0,0,1]]` with `c` and `s` untouched.  ⚠ And the axis
// `(0,0,1)` has length exactly 1.0, so normalising it divides by 1.0 and does
// not disturb that — gated, not assumed.
//
// ⚠ `rig_world_seg` IS UNCHANGED.  Re-expressing it through the 3-D path would
// have moved every existing consumer's numbers by an ulp to buy nothing.

// A bone rig: for each bone its parent (-1 for the root), its joint offset in the parent's
// frame, its length along its own +x, its revolute axis and its joint limits.  `rig_new` and
// `rig_read` make one.
pub struct Rig {
  pub rg_parent: vector<integer>,   // parent bone index; -1 for the root
  pub rg_ox:     vector<float>,     // joint offset in the parent's frame (x)
  pub rg_oy:     vector<float>,     // joint offset in the parent's frame (y)
  pub rg_oz:     vector<float>,     // joint offset in the parent's frame (z) — 0 in a planar rig
  pub rg_len:    vector<float>,     // the bone's length from its joint, along its own +x
  pub rg_ax:     vector<float>,     // the revolute axis, AS GIVEN — see above
  pub rg_ay:     vector<float>,     //   "
  pub rg_az:     vector<float>,     //   " — (0,0,1) in a planar rig: the implicit axis made explicit
  pub rg_lo:     vector<float>,     // the joint's limit low  (turns)
  pub rg_hi:     vector<float>,     // the joint's limit high (turns)
}

// An empty rig.  Add bones to it; every parallel vector stays the same length, which is
// what makes bone index the only identity a caller needs.
pub fn rig_new() -> Rig {
  Rig { rg_parent: [], rg_ox: [], rg_oy: [], rg_oz: [], rg_len: [],
        rg_ax: [], rg_ay: [], rg_az: [], rg_lo: [], rg_hi: [] }
}

// Append one bone in SPACE, growing the rig's parallel field vectors in place.
//
// ⚠ Eleven parameters is the eight of `hex_part::Hinge` plus a parent, a length
// and the rig — the family's own shape rather than a shorter one invented here.
//
// Example: @HXB-004
pub fn rig_bone3(r: Rig, parent: integer, ox: float, oy: float, oz: float, len: float,
                 ax: float, ay: float, az: float, lo: float, hi: float) -> Rig {
  r.rg_parent += [parent];
  r.rg_ox += [ox]; r.rg_oy += [oy]; r.rg_oz += [oz]; r.rg_len += [len];
  r.rg_ax += [ax]; r.rg_ay += [ay]; r.rg_az += [az];
  r.rg_lo += [lo]; r.rg_hi += [hi];
  r
}

// Append one bone in the PLANE — the special case.  A planar bone is one at
// `oz = 0` about `(0, 0, 1)`,
// which is exactly the axis the 2-D code left implicit.
//
// Example: @HXB-001
pub fn rig_bone(r: Rig, parent: integer, ox: float, oy: float, len: float,
                lo: float, hi: float) -> Rig {
  rig_bone3(r, parent, ox, oy, 0.0, len, 0.0, 0.0, 1.0, lo, hi)
}

// How many bones the rig holds — the bound for any walk over it.
pub fn rig_count(r: Rig) -> integer { len(r.rg_parent) }

// Does bone `i` lie in the plane the 2-D rig assumes?  ⚠ It is asked with `==`
// and no tolerance, because it decides which RECORD the bone is written as and
// a near-planar bone must not be written as a planar one.
pub fn bone_planar(r: Rig, i: integer) -> boolean {
  (r.rg_oz[i] ?? 0.0) == 0.0 && (r.rg_ax[i] ?? 0.0) == 0.0
    && (r.rg_ay[i] ?? 0.0) == 0.0 && (r.rg_az[i] ?? 0.0) == 1.0
}

// Is the whole rig planar?  The question `rig_world_seg` implicitly asks of
// every rig it is handed, made available to a caller that wants to know.
//
// Example: @HXB-005
pub fn rig_planar(r: Rig) -> boolean {
  for i in 0..len(r.rg_parent) {
    if !bone_planar(r, i) { return false; }
  }
  true
}

// The joint at bone `i`, as a `Joint` carrying its limits (value defaults to 0 — the neutral pose).
//
// Example: @HXB-002
pub fn rig_joint(r: Rig, i: integer) -> Joint {
  joint_new(0.0, r.rg_lo[i] ?? 0.0, r.rg_hi[i] ?? 0.0)
}

// ADMISSIBLE — topological (root is bone 0 with parent -1, every other parent is earlier), and
// every joint limit is ordered `lo <= hi`.  This is the rig's doorstep: `fits?` for a whole rig.
//
// Example: @HXB-004
// Example: @HXB-008
pub fn rig_admissible(r: Rig) -> boolean {
  n = len(r.rg_parent);
  if n == 0 { return false; }
  if (r.rg_parent[0] ?? 0) != -1 { return false; }
  for i in 0..n {
    p = r.rg_parent[i] ?? 0;
    if i == 0 {
      if p != -1 { return false; }
    } else {
      if p < 0 || p >= i { return false; }        // parent must be an EARLIER bone
    }
    if (r.rg_lo[i] ?? 0.0) > (r.rg_hi[i] ?? 0.0) { return false; }
    // ⚠ AN AXIS OF ZERO LENGTH IS A JOINT WITH NOTHING TO TURN ABOUT —
    // `hex_part::HG_AXIS`, and asked the same way `moros_sim::has_axis` asks
    // it: one component non-zero, never a normalised length.  It is a
    // validation the planar rig had no way to express, because there the axis
    // was implicit and could not be got wrong.
    if (r.rg_ax[i] ?? 0.0) == 0.0 && (r.rg_ay[i] ?? 0.0) == 0.0
       && (r.rg_az[i] ?? 0.0) == 0.0 { return false; }
  }
  true
}

// ── THE FORMAT, AND WHY A SPATIAL BONE CHANGES ITS RECORD TAG ────────────────
//
// A planar bone writes a `bone` record, so a planar rig reads in any version
// that knows `bone`.
//
// ⚠⚠ A spatial bone writes `bone3` rather than a longer `bone` line.  A reader
// that checks the keywords it knows and not the word COUNT would read extra
// fields on a `bone` line as the bone's PLANAR PROJECTION — a rig quietly
// flattened.  Under its own tag the same reader refuses the whole rig, which
// `rig_admissible` then rejects.  Loud beats tidy.
//
// Example: @HXB-008
pub fn rig_write(r: Rig, name: text) -> text {
  n = len(r.rg_parent);
  s = "rig {name} bones {n}";
  for bi in 0..n {
    if bone_planar(r, bi) {
      s = s + "\nbone {bi} parent {r.rg_parent[bi] ?? 0} at {r.rg_ox[bi] ?? 0.0} {r.rg_oy[bi] ?? 0.0}"
          + " len {r.rg_len[bi] ?? 0.0} lim {r.rg_lo[bi] ?? 0.0} {r.rg_hi[bi] ?? 0.0}";
    } else {
      s = s + "\nbone3 {bi} parent {r.rg_parent[bi] ?? 0}"
          + " at {r.rg_ox[bi] ?? 0.0} {r.rg_oy[bi] ?? 0.0} {r.rg_oz[bi] ?? 0.0}"
          + " len {r.rg_len[bi] ?? 0.0}"
          + " axis {r.rg_ax[bi] ?? 0.0} {r.rg_ay[bi] ?? 0.0} {r.rg_az[bi] ?? 0.0}"
          + " lim {r.rg_lo[bi] ?? 0.0} {r.rg_hi[bi] ?? 0.0}";
    }
  }
  s
}

fn rig_word(line: text, i: integer) -> text {
  parts = line.split(' ');
  parts[i] ?? ""
}
fn rig_word_int(line: text, i: integer) -> integer { rig_word(line, i) as integer ?? 0 }
fn rig_word_float(line: text, i: integer) -> float { rig_word(line, i) as float ?? 0.0 }

// How many space-separated fields the line has.  Each record has a fixed shape, so a line
// carrying MORE is a different line — trailing junk and a trailing space both land here.
fn rig_word_count(line: text) -> integer { len(line.split(' ')) }

// Is field `i` a number SPELLED THE WAY `rig_write` spells one?  Not a character class: the
// parse has to reproduce the original characters, which refuses ``, `x`, `007`, `+1.5`, `1.50`
// and `1.5e0` in one test — each of which a fallible cast otherwise hands back as a plausible
// number that then writes out differently.
fn rig_word_is_int(line: text, i: integer) -> boolean {
  w = rig_word(line, i);
  v = w as integer ?? 0;
  "{v}" == w
}
fn rig_word_is_float(line: text, i: integer) -> boolean {
  w = rig_word(line, i);
  v = w as float ?? 0.0;
  "{v}" == w
}

// Every float field of a `bone` / `bone3` record, spelled as this library spells one.
fn rig_floats_ok(line: text, lo: integer, hi: integer) -> boolean {
  for i in lo..hi + 1 {
    if !rig_word_is_float(line, i) { return false; }
  }
  true
}

// STRICT PARSER — accepts exactly what `rig_write` emits, refuses anything else by returning an
// empty rig (which `rig_admissible` then rejects).  A lenient reader would void the byte diff.
//
// What pins that is a RE-SPELLING IDENTITY: a field is a number only if printing the parse
// reproduces the original characters (so `1.0`, `+1.5`, `01.5`, `1.50`, `1.5e0`, an empty
// field and `x` are refused), and a line is a record only if it carries exactly the fields the
// writer emits (so a trailing space or an extra field is refused).
//
// ⚠ WHAT THIS PARSER DOES NOT CHECK IS THE PARENT.  A
// forward, absent or duplicate-root parent round-trips FAITHFULLY — nothing is repaired, the
// byte diff still means what it says — so it is not the reader's to refuse.  It is
// `rig_admissible`'s, which is the rig's doorstep and catches every one of them.  Reading a rig
// is not the same as being allowed to pose it.
//
// Example: @HXB-008
pub fn rig_read(t: text) -> Rig {
  lines = t.split('\n');
  nl = len(lines);
  if nl < 1 { return rig_new(); }
  head = lines[0] ?? "";
  if rig_word(head, 0) != "rig" { return rig_new(); }
  if rig_word(head, 2) != "bones" { return rig_new(); }
  if rig_word_count(head) != 4 { return rig_new(); }
  if !rig_word_is_int(head, 3) { return rig_new(); }
  nb = rig_word_int(head, 3);
  if nl != nb + 1 { return rig_new(); }
  out = rig_new();
  for ri in 1..nl {
    ln = lines[ri] ?? "";
    tag = rig_word(ln, 0);
    if rig_word_int(ln, 1) != ri - 1 { return rig_new(); }     // index order, like form C2
    if rig_word(ln, 2) != "parent" { return rig_new(); }
    if !rig_word_is_int(ln, 1) || !rig_word_is_int(ln, 3) { return rig_new(); }
    if tag == "bone" {
      if rig_word_count(ln) != 12 { return rig_new(); }
      if rig_word(ln, 4) != "at" { return rig_new(); }
      if rig_word(ln, 7) != "len" { return rig_new(); }
      if rig_word(ln, 9) != "lim" { return rig_new(); }
      if !rig_floats_ok(ln, 5, 6) || !rig_word_is_float(ln, 8)
         || !rig_floats_ok(ln, 10, 11) { return rig_new(); }
      out = rig_bone(out, rig_word_int(ln, 3), rig_word_float(ln, 5), rig_word_float(ln, 6),
                     rig_word_float(ln, 8), rig_word_float(ln, 10), rig_word_float(ln, 11));
    } else if tag == "bone3" {
      if rig_word_count(ln) != 17 { return rig_new(); }
      if rig_word(ln, 4) != "at" { return rig_new(); }
      if rig_word(ln, 8) != "len" { return rig_new(); }
      if rig_word(ln, 10) != "axis" { return rig_new(); }
      if rig_word(ln, 14) != "lim" { return rig_new(); }
      if !rig_floats_ok(ln, 5, 7) || !rig_word_is_float(ln, 9)
         || !rig_floats_ok(ln, 11, 13) || !rig_floats_ok(ln, 15, 16) { return rig_new(); }
      out = rig_bone3(out, rig_word_int(ln, 3),
                      rig_word_float(ln, 5), rig_word_float(ln, 6), rig_word_float(ln, 7),
                      rig_word_float(ln, 9),
                      rig_word_float(ln, 11), rig_word_float(ln, 12), rig_word_float(ln, 13),
                      rig_word_float(ln, 15), rig_word_float(ln, 16));
    } else {
      return rig_new();                                        // an unknown record, refused
    }
  }
  out
}

// Byte-exact equality — floats compared with `==`, no tolerance (`I-EXACT`).
pub fn rig_eq(a: Rig, b: Rig) -> boolean {
  n = len(a.rg_parent);
  if len(b.rg_parent) != n { return false; }
  for ei in 0..n {
    if (a.rg_parent[ei] ?? 0) != (b.rg_parent[ei] ?? 0) { return false; }
    if (a.rg_ox[ei] ?? 0.0) != (b.rg_ox[ei] ?? 0.0) { return false; }
    if (a.rg_oy[ei] ?? 0.0) != (b.rg_oy[ei] ?? 0.0) { return false; }
    if (a.rg_oz[ei] ?? 0.0) != (b.rg_oz[ei] ?? 0.0) { return false; }
    if (a.rg_len[ei] ?? 0.0) != (b.rg_len[ei] ?? 0.0) { return false; }
    if (a.rg_ax[ei] ?? 0.0) != (b.rg_ax[ei] ?? 0.0) { return false; }
    if (a.rg_ay[ei] ?? 0.0) != (b.rg_ay[ei] ?? 0.0) { return false; }
    if (a.rg_az[ei] ?? 0.0) != (b.rg_az[ei] ?? 0.0) { return false; }
    if (a.rg_lo[ei] ?? 0.0) != (b.rg_lo[ei] ?? 0.0) { return false; }
    if (a.rg_hi[ei] ?? 0.0) != (b.rg_hi[ei] ?? 0.0) { return false; }
  }
  true
}

// ── THE COLLISION PROXY (phase D of plans/m1-moving-body) ────────────────────
//
// The project's founding claim, applied to the body: **a collision proxy is DERIVED from the
// geometry itself**, not hand-authored.  A rig's geometry is its posed BONE SEGMENTS, so the proxy
// is derived from those: each bone's SHAPE is the capsule (all points within a half-width `w` of
// its segment), and its PROXY is the oriented box that bounds the capsule.  `SPEC` **I4**: a proxy
// **⊇** its shape (never misses an overlap) and its overshoot is **bounded and stated**.
//
// The whole-body pose is FORWARD KINEMATICS up the tree — each bone's world frame composes its
// parent's frame with its joint value — and it is a **PURE FUNCTION of the joint values** (`I6`),
// because it composes `pose_of`, which is pure.  A flexing wing (more bones on more joints, `I-POSE`)
// poses the same way; nothing here deforms.

// The world segment of bone `i` at the given joint values: `(x0, y0)` its base, `(x1, y1)` its tip.
// Composes the tree from the root (bones are topological, so ancestors are computed first).
//
// ⚠⚠ PLANAR RIGS ONLY, and it says so with numbers rather than a refusal.  This walk composes
// joints by ADDING ANGLES, so it reads neither `rg_oz` nor the stored axis: hand it a bone hinged
// about `+y` and it answers as though that hinge were about `+z`.  The rig can be perfectly
// ADMISSIBLE, the call returns four ordinary floats, and nothing says the body it describes is not
// the body you built — measured on a two-bone arm, the tip is 2.5 away at a quarter turn and 3.2 at
// three quarters, on a bone 2 long.  `rig_planar` is the precondition; a spatial rig belongs on
// `rig_world_frame3` / `rig_world_seg3`, and its proxy has to be derived from those.
//
// Example: @HXB-001
// Example: @HXB-005
pub fn rig_world_seg(r: Rig, values: vector<float>, i: integer) -> (float, float, float, float) {
  n = i + 1;
  bx: vector<float> = [];
  by: vector<float> = [];
  th: vector<float> = [];
  for k in 0..n {
    v = values[k] ?? 0.0;
    p = r.rg_parent[k] ?? -1;
    if p < 0 {
      bx += [0.0]; by += [0.0]; th += [v];
    } else {
      pth = th[p] ?? 0.0;
      (rx, ry) = pose_of(pth, r.rg_ox[k] ?? 0.0, r.rg_oy[k] ?? 0.0);   // offset in parent's frame
      bx += [(bx[p] ?? 0.0) + rx];
      by += [(by[p] ?? 0.0) + ry];
      th += [pth + v];                                                 // revolute joints add in 2D
    }
  }
  x0 = bx[i] ?? 0.0;
  y0 = by[i] ?? 0.0;
  (ex, ey) = pose_of(th[i] ?? 0.0, r.rg_len[i] ?? 0.0, 0.0);           // tip = base + len along angle
  (x0, y0, x0 + ex, y0 + ey)
}

// ── FORWARD KINEMATICS IN SPACE ──────────────────────────────────────────────
//
// The composition is the same tree walk one dimension up: a bone's offset rides
// its PARENT's rotation (not its own joint), and its length runs along its own
// `+x` under its OWN rotation.  What changes is that a rotation can no longer
// be carried as an angle, because two rotations about different axes do not add
// — so a 3x3 basis is carried instead, and Rodrigues builds each joint's own.
//
// ⚠ Nine parallel float vectors is the honest cost of a rig with NO
// DEPENDENCIES.  `mesh3d` owns a `Mat4` and is one `use` away, and taking it
// would make the body model depend on a renderer's linear algebra — which the
// manifest's own note refuses (*"the body is pure continuous-space kinematics
// … with NO lattice dependency"*), and a matrix library is the same kind of
// coupling one step further out.
//
// ⚠ A ZERO-LENGTH AXIS POSES AS THE IDENTITY — the joint simply does not turn,
// which is the honest reading of *"nothing to turn about"*.  It is not repaired
// to `+z`: that would invent an axis the author did not write, and
// `rig_admissible` refuses the rig anyway.

// A bone's WORLD FRAME: where its joint sits, and how its own axes lie.
//
// A consumer that draws a body needs it: a limb hung on a bone is a
// cloud of points in the BONE's frame, and posing them means applying this
// once and reusing it, not walking the tree per point.
pub struct Frame {
  pub fr_x: float, pub fr_y: float, pub fr_z: float,      // the bone's joint, in world
  pub fr_00: float, pub fr_01: float, pub fr_02: float,   // its basis, row-major
  pub fr_10: float, pub fr_11: float, pub fr_12: float,
  pub fr_20: float, pub fr_21: float, pub fr_22: float,
}

// The world frame of bone `i` at the given joint values.  ⚠ One tree walk;
// pose as many points through it as you like with `frame_point`.
//
// Example: @HXB-003
pub fn rig_world_frame3(r: Rig, values: vector<float>, i: integer) -> Frame {
  n = i + 1;
  bx: vector<float> = []; by: vector<float> = []; bz: vector<float> = [];
  m00: vector<float> = []; m01: vector<float> = []; m02: vector<float> = [];
  m10: vector<float> = []; m11: vector<float> = []; m12: vector<float> = [];
  m20: vector<float> = []; m21: vector<float> = []; m22: vector<float> = [];
  for k in 0..n {
    th = (values[k] ?? 0.0) * TAU;
    c = cos(th);
    s = sin(th);
    // The axis is stored AS GIVEN, so it is normalised here — at use, by the
    // one consumer that needs a unit vector.  ⚠ `(0,0,1)` has length exactly
    // 1.0 and the divide is exact, which is what keeps a planar single bone
    // bit-identical to `rig_world_seg`.
    ax = r.rg_ax[k] ?? 0.0; ay = r.rg_ay[k] ?? 0.0; az = r.rg_az[k] ?? 0.0;
    al = sqrt(ax * ax + ay * ay + az * az) ?? 0.0;
    ux = 0.0; uy = 0.0; uz = 0.0;
    if al > 0.0 { ux = ax / al ?? 0.0; uy = ay / al ?? 0.0; uz = az / al ?? 0.0; }
    else { c = 1.0; s = 0.0; }                    // no axis: the identity, never a repair
    omc = 1.0 - c;
    l00 = c + omc * ux * ux;       l01 = omc * ux * uy - s * uz;  l02 = omc * ux * uz + s * uy;
    l10 = omc * uy * ux + s * uz;  l11 = c + omc * uy * uy;       l12 = omc * uy * uz - s * ux;
    l20 = omc * uz * ux - s * uy;  l21 = omc * uz * uy + s * ux;  l22 = c + omc * uz * uz;
    p = r.rg_parent[k] ?? -1;
    if p < 0 {
      bx += [0.0]; by += [0.0]; bz += [0.0];
      m00 += [l00]; m01 += [l01]; m02 += [l02];
      m10 += [l10]; m11 += [l11]; m12 += [l12];
      m20 += [l20]; m21 += [l21]; m22 += [l22];
    } else {
      p00 = m00[p] ?? 1.0; p01 = m01[p] ?? 0.0; p02 = m02[p] ?? 0.0;
      p10 = m10[p] ?? 0.0; p11 = m11[p] ?? 1.0; p12 = m12[p] ?? 0.0;
      p20 = m20[p] ?? 0.0; p21 = m21[p] ?? 0.0; p22 = m22[p] ?? 1.0;
      gx = r.rg_ox[k] ?? 0.0; gy = r.rg_oy[k] ?? 0.0; gz = r.rg_oz[k] ?? 0.0;
      bx += [(bx[p] ?? 0.0) + p00 * gx + p01 * gy + p02 * gz];
      by += [(by[p] ?? 0.0) + p10 * gx + p11 * gy + p12 * gz];
      bz += [(bz[p] ?? 0.0) + p20 * gx + p21 * gy + p22 * gz];
      m00 += [p00 * l00 + p01 * l10 + p02 * l20];
      m01 += [p00 * l01 + p01 * l11 + p02 * l21];
      m02 += [p00 * l02 + p01 * l12 + p02 * l22];
      m10 += [p10 * l00 + p11 * l10 + p12 * l20];
      m11 += [p10 * l01 + p11 * l11 + p12 * l21];
      m12 += [p10 * l02 + p11 * l12 + p12 * l22];
      m20 += [p20 * l00 + p21 * l10 + p22 * l20];
      m21 += [p20 * l01 + p21 * l11 + p22 * l21];
      m22 += [p20 * l02 + p21 * l12 + p22 * l22];
    }
  }
  Frame { fr_x: bx[i] ?? 0.0, fr_y: by[i] ?? 0.0, fr_z: bz[i] ?? 0.0,
          fr_00: m00[i] ?? 1.0, fr_01: m01[i] ?? 0.0, fr_02: m02[i] ?? 0.0,
          fr_10: m10[i] ?? 0.0, fr_11: m11[i] ?? 1.0, fr_12: m12[i] ?? 0.0,
          fr_20: m20[i] ?? 0.0, fr_21: m21[i] ?? 0.0, fr_22: m22[i] ?? 1.0 }
}

// Where a point in the bone's LOCAL frame lands in the world.
//
// Example: @HXB-003
pub fn frame_point(f: Frame, lx: float, ly: float, lz: float) -> (float, float, float) {
  (f.fr_x + f.fr_00 * lx + f.fr_01 * ly + f.fr_02 * lz,
   f.fr_y + f.fr_10 * lx + f.fr_11 * ly + f.fr_12 * lz,
   f.fr_z + f.fr_20 * lx + f.fr_21 * ly + f.fr_22 * lz)
}

// The one-shot door: pose ONE point of bone `i`.  ⚠ It walks the tree, so
// a caller posing many points should hold a `Frame` instead.
//
// Example: @HXB-003
pub fn rig_world_point3(r: Rig, values: vector<float>, i: integer,
                        lx: float, ly: float, lz: float) -> (float, float, float) {
  frame_point(rig_world_frame3(r, values, i), lx, ly, lz)
}

// The world segment of bone `i` IN THREE DIMENSIONS: `(x0,y0,z0)` its base,
// `(x1,y1,z1)` its tip.  The 2-D `rig_world_seg` is a separate walk — see the
// header for why.
//
// It is TWO POINTS of the bone's own frame: its base is the frame's origin and
// its tip is `(len, 0, 0)` in it.
//
// Example: @HXB-003
pub fn rig_world_seg3(r: Rig, values: vector<float>, i: integer)
    -> (float, float, float, float, float, float) {
  f = rig_world_frame3(r, values, i);
  (bx, by, bz) = frame_point(f, 0.0, 0.0, 0.0);
  (tx, ty, tz) = frame_point(f, r.rg_len[i] ?? 0.0, 0.0, 0.0);
  (bx, by, bz, tx, ty, tz)
}

// An oriented bounding box: centre, unit axis (along the bone), and half-extents.
pub struct Obb {
  pub ob_cx: float, pub ob_cy: float,   // centre
  pub ob_ux: float, pub ob_uy: float,   // unit axis, along the segment
  pub ob_hl: float,                 // half-length, along the axis
  pub ob_hw: float,                 // half-width, perpendicular
}

// THE PROXY of bone `i`: the OBB that bounds its capsule of half-width `w`.  Length grows by `w`
// at each end to cover the rounded caps; width is `w`.
//
// ⚠ IT IS BUILT ON `rig_world_seg`, so it is PLANAR-ONLY for the same reason and inherits the same
// silence: on a spatial rig this boxes a bone that is somewhere else, and `I4`'s containment — a
// proxy never misses an overlap — does not hold.  Check `rig_planar` first.
//
// Example: @HXB-005
// Example: @HXB-006
pub fn bone_obb(r: Rig, values: vector<float>, i: integer, w: float) -> Obb {
  (x0, y0, x1, y1) = rig_world_seg(r, values, i);
  dx = x1 - x0;
  dy = y1 - y0;
  seg = sqrt(dx * dx + dy * dy) ?? 0.0;
  ux = 1.0;
  uy = 0.0;
  if seg > 0.000000001 { ux = dx / seg ?? 1.0; uy = dy / seg ?? 0.0; }
  Obb { ob_cx: (x0 + x1) / 2.0, ob_cy: (y0 + y1) / 2.0,
        ob_ux: ux, ob_uy: uy, ob_hl: seg / 2.0 + w, ob_hw: w }
}

// Is a world point inside the OBB?  Project onto the axis and its perpendicular.
pub fn obb_contains(o: Obb, px: float, py: float) -> boolean {
  dx = px - o.ob_cx;
  dy = py - o.ob_cy;
  along = dx * o.ob_ux + dy * o.ob_uy;
  perp = 0.0 - dx * o.ob_uy + dy * o.ob_ux;
  fabs(along) <= o.ob_hl && fabs(perp) <= o.ob_hw
}

// Distance from a point to a segment — the capsule test's core.
pub fn seg_dist(x0: float, y0: float, x1: float, y1: float, px: float, py: float) -> float {
  dx = x1 - x0;
  dy = y1 - y0;
  l2 = dx * dx + dy * dy;
  if l2 < 0.000000000001 { return sqrt((px - x0) * (px - x0) + (py - y0) * (py - y0)) ?? 0.0; }
  t = ((px - x0) * dx + (py - y0) * dy) / l2 ?? 0.0;
  if t < 0.0 { t = 0.0; }
  if t > 1.0 { t = 1.0; }
  cx = x0 + t * dx;
  cy = y0 + t * dy;
  sqrt((px - cx) * (px - cx) + (py - cy) * (py - cy)) ?? 0.0
}

// The bone's SHAPE: is a world point within `w` of its segment (inside the capsule)?
//
// ⚠ Planar-only, like `bone_obb`: it asks `rig_world_seg`.
//
// Example: @HXB-006
pub fn bone_shape_has(r: Rig, values: vector<float>, i: integer, w: float,
                      px: float, py: float) -> boolean {
  (x0, y0, x1, y1) = rig_world_seg(r, values, i);
  seg_dist(x0, y0, x1, y1, px, py) <= w
}

tests/01-hex-body.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// body — the conformance tests: a pose is a PURE FUNCTION of joint values, a wheel rolls without
// slip, a rig round-trips, and a bone's proxy contains its shape.
//
// These assert the library's CONTRACT.  The exhaustive measurement lives in hexbody's
// `tests/joint.loft`, which prints its evidence and carries a control that fires; this is what CI
// runs under `LOFT_DENY_WARNINGS=1`.
use hex_body::*;

fn fabs2(x: float) -> float { if x < 0.0 { 0.0 - x } else { x } }

fn main() {
  (x, y) = pose_of(0.25, 1.0, 0.0);                  // quarter turn of (1,0) -> ~(0,1)
  assert(fabs2(x) < 0.0001 && y > 0.9999, "a quarter turn takes (1,0) to (0,1)");
}

fn test_a_pose_is_a_pure_function_of_the_joint_value() {
  // I6: two paths to the same value give a byte-identical pose. pose_of reads no history.
  (ax, ay) = pose_of(0.37, 1.4, -0.6);
  pose_of(0.1, 1.4, -0.6);                            // visit other values
  pose_of(0.62, 1.4, -0.6);
  (bx, by) = pose_of(0.37, 1.4, -0.6);
  assert(ax == bx && ay == by, "the pose is byte-identical across two paths to the value");
}

fn test_a_wheel_rolls_without_slip() {
  // The value is derived from travel, so skid = |r*theta - travel| is machine-eps.
  r = 2.5;
  worst = 0.0;
  for i in 0..21 {
    travel = (i as float) * 0.37;
    sk = wheel_skid(r, wheel_value(travel, r, 0.0), travel);
    if sk > worst { worst = sk; }
  }
  assert(worst < 0.000000000001, "a derived wheel value does not slip beyond machine eps");
  // control: spin off-travel and it skids by whole units.
  off = wheel_skid(r, wheel_value(10.0, r, 0.0), 3.0);
  assert(off > 1.0, "an off-travel spin skids");
}

fn test_a_joint_refuses_out_of_range_with_an_offer() {
  // K-FIT: a joint value is ordinal, so a refusal offers the nearest admissible.
  h = joint_new(0.0, -0.25, 0.25);
  assert(joint_fits(h, 0.1), "0.1 is within +-0.25");
  assert(!joint_fits(h, 0.9), "0.9 is not");
  assert(joint_offer(h, 0.9) == 0.25, "the offer is the nearest limit");
  assert(joint_fits(h, joint_offer(h, 0.9)), "and the offer is itself admissible");
}

fn test_a_rig_round_trips_as_canonical_text() {
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 1.5, -0.5, 0.5);
  r = rig_bone(r, 0, 0.8660254037844386, 0.25, 0.37, -0.125, 0.125);
  t = rig_write(r, "arm");
  back = rig_read(t);
  assert(rig_eq(r, back), "write(read(R)) = R byte-for-byte, floats included");
  assert(rig_write(back, "arm") == t, "and the bytes match");
  assert(rig_admissible(r), "a topological rig with ordered limits is admissible");
  // a reordered bone index is refused, not repaired.
  bad = "rig arm bones 2\nbone 1 parent -1 at 0 0 len 1 lim 0 0\nbone 0 parent 0 at 0 0 len 1 lim 0 0";
  assert(rig_count(rig_read(bad)) == 0, "a malformed rig is refused");
}

fn test_a_bone_proxy_contains_its_shape() {
  // I4: the derived OBB proxy contains the bone's capsule; a shape point outside it would be a
  // missed overlap.
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 3.0, -1.0, 1.0);
  r = rig_bone(r, 0, 3.0, 0.0, 2.0, -0.25, 0.25);
  vals = [0.0, 0.125];
  w = 0.3;
  o = bone_obb(r, vals, 1, w);
  (x0, y0, x1, y1) = rig_world_seg(r, vals, 1);
  miss = 0;
  ns = 0;
  for gi in 0..41 {
    for gj in 0..29 {
      gx = 2.0 + (gi as float) * 0.06;
      gy = -0.5 + (gj as float) * 0.06;
      if seg_dist(x0, y0, x1, y1, gx, gy) <= w {
        ns = ns + 1;
        if !obb_contains(o, gx, gy) { miss = miss + 1; }
      }
    }
  }
  assert(ns > 50, "the grid covers the bone");
  assert(miss == 0, "every shape point is inside the proxy (proxy contains the shape)");
  // control: a shrunk proxy no longer contains an edge point of the shape.
  edge = obb_contains(o, o.ob_cx - 0.9 * w * o.ob_uy, o.ob_cy + 0.9 * w * o.ob_ux);
  o2 = Obb { ob_cx: o.ob_cx, ob_cy: o.ob_cy, ob_ux: o.ob_ux, ob_uy: o.ob_uy,
             ob_hl: o.ob_hl, ob_hw: w * 0.5 };
  shrunk = obb_contains(o2, o.ob_cx - 0.9 * w * o.ob_uy, o.ob_cy + 0.9 * w * o.ob_ux);
  assert(edge && !shrunk, "shrinking the proxy below the shape drops an edge point (a missed overlap)");
}

tests/02-rig-bone3.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// body `G1b` — the third dimension: an explicit revolute AXIS and an `oz`.
//
// ── The one claim ────────────────────────────────────────────────────────────
//
// > **`rig_bone3` is a strict GENERALISATION: a planar rig behaves exactly as
// > it did, and its bytes are exactly the bytes 0.1.0 wrote.**
//
// Everything below is that claim taken apart.  ⚠ The interesting half is where
// the generalisation stops being exact and why — `rig_world_seg` composes by
// ADDING ANGLES while any 3-D form composes ROTATIONS, so a CHAIN cannot be
// bit-identical in f64 and a single bone can.  That boundary is measured here
// rather than asserted, and it is the reason the implementation carries a
// matrix and not a quaternion.
use hex_body::*;

fn fabs3(x: float) -> float { if x < 0.0 { 0.0 - x } else { x } }

fn worse(a: float, b: float) -> float { if a > b { a } else { b } }

// The 0.1.0 fixture, verbatim from `01-hex-body.loft`.
fn planar_arm() -> Rig {
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 1.5, -0.5, 0.5);
  r = rig_bone(r, 0, 0.8660254037844386, 0.25, 0.37, -0.125, 0.125);
  r
}

// ─────────────────────────────────────────────────────────────────────────────
// The format — the compatibility half
// ─────────────────────────────────────────────────────────────────────────────

// ⚠⚠ THE COMPATIBILITY GATE, and it is a byte literal on purpose.  Every other
// assertion in this file would still pass if the planar record grew a field;
// only pinning the exact bytes 0.1.0 wrote can see that.
fn test_a_planar_rig_writes_the_bytes_zero_one_wrote() {
  want = "rig arm bones 2"
       + "\nbone 0 parent -1 at 0 0 len 1.5 lim -0.5 0.5"
       + "\nbone 1 parent 0 at 0.8660254037844386 0.25 len 0.37 lim -0.125 0.125";
  got = rig_write(planar_arm(), "arm");
  assert(got == want, "a planar rig's bytes are unchanged across the version.\nwant: {want}\ngot:  {got}");
}

// ⚠⚠ A SPATIAL BONE CHANGES ITS RECORD TAG, and the reason is a measurement of
// the SHIPPED 0.1.0 reader: its parser checks the keywords it knows and never
// the word COUNT, so a longer `bone` line is read as the bone's PLANAR
// PROJECTION — a rig silently flattened.  Under `bone3` the same reader refuses
// the whole rig instead.  Loud beats tidy.
fn test_a_spatial_bone_changes_its_record_tag() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.3, 1.5, 1.0, 0.0, 0.0, -0.5, 0.5);
  want = "rig lid bones 1"
       + "\nbone3 0 parent -1 at 0 0 0.3 len 1.5 axis 1 0 0 lim -0.5 0.5";
  got = rig_write(r, "lid");
  assert(got == want, "a spatial bone is its own record.\nwant: {want}\ngot:  {got}");

  // the control: the SAME call with the planar defaults writes the 0.1.0 record
  q = rig_new();
  q = rig_bone3(q, -1, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, 1.0, -0.5, 0.5);
  assert(rig_write(q, "lid") == "rig lid bones 1\nbone 0 parent -1 at 0 0 len 1.5 lim -0.5 0.5",
         "and a bone3 built at the planar defaults is a planar bone: {rig_write(q, \"lid\")}");
}

fn test_a_spatial_rig_round_trips_as_canonical_text() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, 1.0, -0.5, 0.5);
  r = rig_bone3(r, 0, 1.5, 0.0, 0.42, 0.6, 0.0, 1.0, 0.0, 0.0, 0.30);
  t = rig_write(r, "hover");
  back = rig_read(t);
  assert(rig_eq(r, back), "write(read(R)) = R byte-for-byte, the axis included");
  assert(rig_write(back, "hover") == t, "and the bytes match");
  assert(rig_admissible(r), "a mixed planar/spatial rig is admissible");

}

// ⚠⚠ `rig_eq` has to SEE each new field SEPARATELY, or the round trip above is
// vacuous — and this test was WRONG on its first writing in exactly the way
// that matters.  It compared a rig with axis `(0,1,0)` against one with
// `(1,0,0)`, which differ in TWO components: deleting the `rg_ax` comparison
// left the `rg_ay` one to catch it, and the break passed.  A negative control
// has to be MINIMAL — one field apart — or it tests the whole set at once and
// can see none of them.
fn test_rig_eq_sees_each_new_field_on_its_own() {
  base = one_spatial_bone(0.42, 0.0, 1.0, 0.0);
  assert(rig_eq(base, one_spatial_bone(0.42, 0.0, 1.0, 0.0)),
         "the control: two identical rigs ARE equal, so the four below are not vacuous");
  assert(!rig_eq(base, one_spatial_bone(0.43, 0.0, 1.0, 0.0)), "oz alone is seen");
  assert(!rig_eq(base, one_spatial_bone(0.42, 0.5, 1.0, 0.0)), "ax alone is seen");
  assert(!rig_eq(base, one_spatial_bone(0.42, 0.0, 1.5, 0.0)), "ay alone is seen");
  assert(!rig_eq(base, one_spatial_bone(0.42, 0.0, 1.0, 0.5)), "az alone is seen");
}

// One root plus one child that differs only in the four fields under test.
fn one_spatial_bone(oz: float, ax: float, ay: float, az: float) -> Rig {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, 1.0, -0.5, 0.5);
  r = rig_bone3(r, 0, 1.5, 0.0, oz, 0.6, ax, ay, az, 0.0, 0.30);
  r
}

fn test_an_unknown_record_is_refused() {
  bad = "rig arm bones 1\nbone9 0 parent -1 at 0 0 len 1 lim 0 0";
  assert(rig_count(rig_read(bad)) == 0, "a record this reader does not know is refused");
  ok = "rig arm bones 1\nbone 0 parent -1 at 0 0 len 1 lim 0 0";
  assert(rig_count(rig_read(ok)) == 1, "the control: the record it does know is read");
}

// ─────────────────────────────────────────────────────────────────────────────
// The pose — where the generalisation is exact, and where it stops
// ─────────────────────────────────────────────────────────────────────────────

// ⚠⚠ THE BOUNDARY, MEASURED.  A single planar bone poses IDENTICALLY through
// both functions; a planar CHAIN cannot, because `rig_world_seg` adds angles
// and `rig_world_seg3` composes rotations.  This pins both halves, so a future
// change that quietly widens the gap fails here.
fn test_the_planar_pose_survives_the_third_dimension() {
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 1.5, -1.0, 1.0);
  r = rig_bone(r, 0, 1.3, 0.25, 0.37, -1.0, 1.0);
  r = rig_bone(r, 1, 0.7, -0.6, 0.82, -1.0, 1.0);
  vals = [0.13, -0.29, 0.41];

  // the ROOT is exact — Rodrigues at (0,0,1) collapses to [[c,-s,0],[s,c,0],
  // [0,0,1]] with c and s untouched, and normalising a unit axis divides by 1.0
  (_, _, ax1, ay1) = rig_world_seg(r, vals, 0);
  (_, _, _, bx1, by1, bz1) = rig_world_seg3(r, vals, 0);
  assert(ax1 == bx1 && ay1 == by1,
         "a single planar bone poses BIT-IDENTICALLY: ({ax1}, {ay1}) vs ({bx1}, {by1})");
  assert(bz1 == 0.0, "and never leaves the plane");

  // the CHAIN agrees to within an ulp of the coordinate, and no better
  worst = 0.0;
  for i in 0..3 {
    (_, _, px, py) = rig_world_seg(r, vals, i);
    (_, _, _, qx, qy, qz) = rig_world_seg3(r, vals, i);
    worst = worse(worst, worse(fabs3(qx - px), fabs3(qy - py)));
    assert(qz == 0.0, "bone {i} of a planar rig stays at z = 0");
  }
  assert(worst < 0.000000000000001,
         "a planar chain agrees to within 1e-15 m, got {worst}");

  // ⚠ THE CONTROL: can this comparison produce a non-trivial reading at all?
  // The same instrument against a DIFFERENT pose must read metres.
  (_, _, cx, cy) = rig_world_seg(r, vals, 2);
  (_, _, _, dx, dy, _) = rig_world_seg3(r, [0.13, -0.29, 0.9], 2);
  assert(fabs3(dx - cx) + fabs3(dy - cy) > 0.5,
         "the control: a real difference reads in metres, not ulps");
}

// ⚠ The thing a planar rig CANNOT express, which is why this phase exists: a
// bone hinged about an axis perpendicular to itself swings OUT of the plane.
fn test_a_lateral_hinge_leaves_the_plane() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -0.5, 0.5);
  (_, _, _, x, y, z) = rig_world_seg3(r, [0.25], 0);
  // a quarter turn about +y takes the bone's own +x to -z
  assert(fabs3(x) < 0.000000000000001 && fabs3(y) < 0.000000000000001,
         "a quarter turn about +y empties x and y: ({x}, {y})");
  assert(fabs3(z + 1.0) < 0.000000000000001, "and puts the tip at z = -1, got {z}");

  // the control: the SAME rig about the PLANAR axis never leaves z = 0
  q = rig_new();
  q = rig_bone3(q, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, -0.5, 0.5);
  (_, _, _, _, _, qz) = rig_world_seg3(q, [0.25], 0);
  assert(qz == 0.0, "a planar hinge cannot express this at all, which is the point");
}

// ⚠ The other half of the third dimension, and it is a separate mechanism from
// the axis: an OFFSET in z.  Without this the round trip proves `oz` is STORED
// and nothing proves it is USED.
fn test_an_offset_in_z_lifts_the_bone() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, -0.5, 0.5);
  r = rig_bone3(r, 0, 0.0, 0.0, 0.42, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5);
  (bx, by, bz, tx, ty, tz) = rig_world_seg3(r, [0.0, 0.0], 1);
  assert(bz == 0.42, "the child's base sits at its own oz, got {bz}");
  assert(tz == 0.42, "and its tip stays there, since it turns about +z: {tz}");
  assert(bx == 0.0 && by == 0.0, "with x and y untouched: ({bx}, {by})");
  assert(tx == 0.5 && ty == 0.0, "and the length still runs along +x: ({tx}, {ty})");

  // ⚠ The offset rides the PARENT's rotation, not its own — the same rule the
  // 2-D code follows, and a z offset under a planar parent is unmoved by it.
  (_, _, qz, _, _, _) = rig_world_seg3(r, [0.25, 0.0], 1);
  assert(qz == 0.42, "turning the parent about +z leaves a z offset where it was: {qz}");
}

// ⚠ The axis is stored AS GIVEN and normalised only at use — `hex_part`'s rule,
// and this is the one test that can tell the two apart.
fn test_the_axis_is_stored_as_given_and_normalised_at_use() {
  twice = rig_new();
  twice = rig_bone3(twice, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, -0.5, 0.5);
  assert(rig_write(twice, "a").contains("axis 0 2 0"),
         "the number the author wrote is the number stored: {rig_write(twice, \"a\")}");

  unit = rig_new();
  unit = rig_bone3(unit, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -0.5, 0.5);
  (_, _, _, ux, uy, uz) = rig_world_seg3(unit, [0.3], 0);
  (_, _, _, tx, ty, tz) = rig_world_seg3(twice, [0.3], 0);
  assert(ux == tx && uy == ty && uz == tz,
         "and a longer axis poses identically, because it is normalised at use");
}

// ⚠ A validation the planar rig had no way to express: there, the axis was
// implicit and could not be got wrong.
fn test_an_axis_of_zero_length_is_refused() {
  bad = rig_new();
  bad = rig_bone3(bad, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -0.5, 0.5);
  assert(!rig_admissible(bad), "a joint with nothing to turn about is not admissible");

  // the control: one non-zero component is enough, exactly as `has_axis` asks
  ok = rig_new();
  ok = rig_bone3(ok, -1, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.000001, -0.5, 0.5);
  assert(rig_admissible(ok), "and a small axis is still an axis");
  assert(rig_admissible(planar_arm()), "and every 0.1.0 rig is still admissible");

  // ⚠ It poses as the IDENTITY rather than being repaired to +z — the tip stays
  // on the bone's own +x however far the joint is driven.
  (_, _, _, bx, by, bz) = rig_world_seg3(bad, [0.25], 0);
  assert(bx == 1.0 && by == 0.0 && bz == 0.0,
         "an axis-less joint does not turn: ({bx}, {by}, {bz})");
}

// ⚠⚠ THE API BREAK, PINNED — 0.2.0 is NOT a drop-in for 0.1.0, and this is the
// one shape in which that bites.  `struct Rig` is public and gained four
// fields, so a consumer who built one by LITERAL (the six-field shape
// `rig_new` itself uses) now omits them, and under [loft#914] they default
// silently to empty vectors.  An absent axis reads as `(0,0,0)`.
//
// ⚠ It is REFUSED rather than repaired, deliberately: treating an absent axis
// as the planar default would be a silent repair, and this library refuses
// those everywhere else.  A rig whose axis nobody wrote should be refused
// loudly — but a 0.1.0 consumer got `true` here, so the manifest declares the
// break (`api_compatible_with = "0.2.0"`) instead of pretending otherwise.
fn test_a_zero_one_shaped_literal_is_refused_and_that_is_the_break() {
  old = Rig { rg_parent: [-1], rg_ox: [0.0], rg_oy: [0.0],
              rg_len: [1.5], rg_lo: [-0.5], rg_hi: [0.5] };
  assert(rig_count(old) == 1, "the literal still builds a one-bone rig");
  assert(!rig_admissible(old),
         "and it is now REFUSED — 0.1.0 answered true, which is the declared break");

  // ⚠ The 2-D path is untouched by it: `rig_world_seg` never reads an axis, so
  // a consumer that only poses in the plane keeps working.  That is why
  // `loft compat test` reports the 0.1.0 suite still passing.
  (px, py, tx, ty) = rig_world_seg(old, [0.0], 0);
  assert(px == 0.0 && py == 0.0 && tx == 1.5 && ty == 0.0,
         "the planar pose is unchanged: ({px}, {py}) -> ({tx}, {ty})");

  // the control: the SAME rig through the constructors is admissible
  good = rig_new();
  good = rig_bone(good, -1, 0.0, 0.0, 1.5, -0.5, 0.5);
  assert(rig_admissible(good),
         "and every consumer that builds through rig_new + rig_bone is unaffected");
}

// ⚠ `bone_planar` decides which RECORD a bone is written as, so it is asked
// with `==` and no tolerance.  A near-planar bone is a spatial bone.
fn test_a_nearly_planar_bone_is_a_spatial_bone() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0000000001, 1.0, 0.0, 0.0, 1.0, -0.5, 0.5);
  assert(!bone_planar(r, 0), "a bone a tenth of a nanometre out of plane is not planar");
  assert(rig_write(r, "a").contains("bone3"), "so it writes the spatial record");
  assert(!rig_planar(r), "and the rig is not planar either");
  assert(rig_planar(planar_arm()), "the control: the 0.1.0 fixture is planar");
}

// ─────────────────────────────────────────────────────────────────────────────
// The world FRAME (0.3.0) — what `rig_world_seg3` used to throw away
// ─────────────────────────────────────────────────────────────────────────────

// ⚠ The frame IS the segment: its origin is the bone's base and `(len,0,0)`
// in it is the tip.  If these ever disagree there are two implementations of
// the walk, which is the thing 0.3.0 removed.
fn test_the_frame_and_the_segment_are_one_walk() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, 1.0, -1.0, 1.0);
  r = rig_bone3(r, 0, 1.5, 0.0, 0.4, 0.6, 0.0, 1.0, 0.0, -1.0, 1.0);
  vals = [0.17, -0.31];

  (bx, by, bz, tx, ty, tz) = rig_world_seg3(r, vals, 1);
  f = rig_world_frame3(r, vals, 1);
  (fx, fy, fz) = frame_point(f, 0.0, 0.0, 0.0);
  (gx, gy, gz) = frame_point(f, 0.6, 0.0, 0.0);

  assert(bx == fx && by == fy && bz == fz,
         "the frame's origin IS the bone's base: ({bx},{by},{bz}) vs ({fx},{fy},{fz})");
  assert(tx == gx && ty == gy && tz == gz,
         "and (len,0,0) in it IS the tip: ({tx},{ty},{tz}) vs ({gx},{gy},{gz})");

  // the one-shot door agrees with the held frame
  (px, py, pz) = rig_world_point3(r, vals, 1, 0.2, -0.3, 0.4);
  (qx, qy, qz) = frame_point(f, 0.2, -0.3, 0.4);
  assert(px == qx && py == qy && pz == qz, "both doors answer the same point");
}

// ⚠ A frame's basis is a ROTATION — orthonormal, right-handed.  A consumer
// posing a limb through it must not have its geometry sheared or mirrored,
// and nothing else in this file would notice if it were.
fn test_a_frames_basis_is_a_rotation() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 1.0, 0.3, -0.7, 0.4, -1.0, 1.0);
  r = rig_bone3(r, 0, 0.5, 0.2, 0.1, 1.0, 0.0, 1.0, 0.0, -1.0, 1.0);
  f = rig_world_frame3(r, [0.21, 0.43], 1);

  // each column is a unit vector
  c0 = f.fr_00 * f.fr_00 + f.fr_10 * f.fr_10 + f.fr_20 * f.fr_20;
  c1 = f.fr_01 * f.fr_01 + f.fr_11 * f.fr_11 + f.fr_21 * f.fr_21;
  c2 = f.fr_02 * f.fr_02 + f.fr_12 * f.fr_12 + f.fr_22 * f.fr_22;
  assert(fabs3(c0 - 1.0) < 0.000000000001, "column 0 is unit: {c0}");
  assert(fabs3(c1 - 1.0) < 0.000000000001, "column 1 is unit: {c1}");
  assert(fabs3(c2 - 1.0) < 0.000000000001, "column 2 is unit: {c2}");

  // and they are mutually perpendicular
  d01 = f.fr_00 * f.fr_01 + f.fr_10 * f.fr_11 + f.fr_20 * f.fr_21;
  d02 = f.fr_00 * f.fr_02 + f.fr_10 * f.fr_12 + f.fr_20 * f.fr_22;
  d12 = f.fr_01 * f.fr_02 + f.fr_11 * f.fr_12 + f.fr_21 * f.fr_22;
  assert(fabs3(d01) < 0.000000000001 && fabs3(d02) < 0.000000000001
         && fabs3(d12) < 0.000000000001,
         "the columns are perpendicular: {d01}, {d02}, {d12}");

  // ⚠ RIGHT-handed: column0 x column1 = column2.  A mirrored basis is
  // orthonormal too, so the check above cannot see one.
  cx = f.fr_10 * f.fr_21 - f.fr_20 * f.fr_11;
  cy = f.fr_20 * f.fr_01 - f.fr_00 * f.fr_21;
  cz = f.fr_00 * f.fr_11 - f.fr_10 * f.fr_01;
  assert(fabs3(cx - f.fr_02) < 0.000000000001
         && fabs3(cy - f.fr_12) < 0.000000000001
         && fabs3(cz - f.fr_22) < 0.000000000001,
         "and right-handed: ({cx},{cy},{cz}) vs ({f.fr_02},{f.fr_12},{f.fr_22})");
}

// ⚠⚠ THE ONE A CONSUMER ACTUALLY NEEDS: a point OFF the bone's axis, posed.
// `rig_world_seg3` could never answer this — the tip is on the axis — and it
// is exactly what a limb hung on a hinge is.
fn test_a_point_off_the_axis_swings_where_trigonometry_says() {
  r = rig_new();
  r = rig_bone3(r, -1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
  // a lateral hinge, like a canopy's
  r = rig_bone3(r, 0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5);

  // a point 1 m FORWARD of the hinge, shut
  (sx, sy, sz) = rig_world_point3(r, [0.0, 0.0], 1, 0.0, 1.0, 0.0);
  assert(fabs3(sx) < 0.000000000001 && fabs3(sy - 1.0) < 0.000000000001
         && fabs3(sz - 1.0) < 0.000000000001,
         "shut, it is 1 m forward at the hinge's height: ({sx},{sy},{sz})");

  // a quarter turn about +x takes +y to +z
  (ox, oy, oz) = rig_world_point3(r, [0.0, 0.25], 1, 0.0, 1.0, 0.0);
  assert(fabs3(ox) < 0.000000000001 && fabs3(oy) < 0.000000000001
         && fabs3(oz - 2.0) < 0.000000000001,
         "a quarter turn stands it straight up, 1 m above the hinge: ({ox},{oy},{oz})");

  // ⚠ TURNS, NOT DEGREES — the assertion that can tell them apart.  At 0.25
  // read as degrees the point would barely move; here it travels a quarter of
  // the way round, 1.414 m from where it started.
  dy = oy - sy; dz = oz - sz;
  moved = sqrt(dy * dy + dz * dz) ?? 0.0;
  assert(moved > 1.41 && moved < 1.42,
         "0.25 is a QUARTER TURN and moves it {moved} m, not a fraction of a degree");
}

tests/03-worked-examples.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Worked examples for `hex_body` (@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: @HXB-00N`, and
// `check_doc_drift.sh examples` fails if a citation goes dangling.  These are
// teaching examples, distinct from the conformance suites in `01-hex-body.loft`
// and `02-rig-bone3.loft`.
//
// WHAT THIS PACKAGE ASKS OF A CALLER, and why a signature cannot carry it.  A
// body here is a RIG — bones, and the limits in the joints between them — and
// never a pose.  Everything follows from that one decision:
//
//   the pose is COMPUTED     from a joint-value vector you pass in, by a pure
//                            function, so two callers can pose one rig two ways
//                            at the same instant and neither disturbs the other
//   the values are YOURS     positional, unchecked, and silently defaulted —
//                            the joint limits are a doorstep the caller calls
//   there are TWO WALKS      a 2-D one that adds angles and a 3-D one that
//                            composes rotations, and they are separate ON PURPOSE
//
// The rigs below are two and three bones at quarter turns, so every world
// position in these tests is one you can work out on paper.
use hex_body::*;

const W = 0.5;                 // the half-width every proxy example uses

fn fabs3(x: float) -> float { if x < 0.0 { 0.0 - x } else { x } }

// The fixture: an arm.  Bone 0 is 3 long from the origin; bone 1 hangs off its
// tip and is 2 long.  Both are planar — the axis every 2-D rig leaves implicit.
fn arm() -> Rig {
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 3.0, 0.0 - 1.0, 1.0);
  r = rig_bone(r, 0, 3.0, 0.0, 2.0, 0.0 - 0.25, 0.25);
  r
}

// The same arm with its second bone hinged about +y instead — out of the plane,
// and 0.5 above it.  An admissible rig that the 2-D walk cannot represent.
fn spatial_arm() -> Rig {
  r = rig_new();
  r = rig_bone(r, -1, 0.0, 0.0, 3.0, 0.0 - 1.0, 1.0);
  r = rig_bone3(r, 0, 3.0, 0.0, 0.5, 2.0, 0.0, 1.0, 0.0, 0.0 - 1.0, 1.0);
  r
}

// The smallest call that does something real: pose an arm and read where its
// forearm ended up.
fn main() {
  r = arm();
  (x0, y0, x1, y1) = rig_world_seg(r, [0.0, 0.25], 1);
  assert(x0 == 3.0 && y0 == 0.0, "the forearm hangs off the upper arm's tip");
  assert(x1 == 3.0 && y1 == 2.0, "and a quarter turn stands it straight up, got ({x1},{y1})");
}

// @HXB-001 — A BODY IS A RIG, NEVER A POSE, and the type says so by having
// nowhere to put one.  `Rig` carries bones and joint limits; the joint VALUES
// arrive as a separate vector at every query, and computing a pose writes
// nothing back.  So two callers can hold one rig and pose it differently at the
// same instant — a server stepping the authoritative body while a renderer
// draws last frame's — and neither sees the other.
//
// The computation is a PURE function of `(rig, values)`: no elapsed time, no
// call count, no previous value.  That is what makes a pose replayable, and it
// is the scope boundary the package draws — it answers where a part is NOW,
// never what it will do next.
fn test_a_body_is_a_rig_and_the_pose_is_computed() {
  r = arm();
  straight = [0.0, 0.0];
  bent = [0.0, 0.25];

  (_a0, _b0, sx, sy) = rig_world_seg(r, straight, 1);
  (_c0, _d0, bx, by) = rig_world_seg(r, bent, 1);
  assert(sx == 5.0 && sy == 0.0, "straight out, the tip is 3 + 2 along x, got ({sx},{sy})");
  assert(bx == 3.0 && by == 2.0, "bent a quarter turn, it is 2 up from the elbow, got ({bx},{by})");

  // Posing one way did not disturb the other: ask again in the other order.
  (_e0, _f0, sx2, sy2) = rig_world_seg(r, straight, 1);
  assert(sx2 == sx && sy2 == sy, "the same values give byte-identical numbers, always");

  // …and the rig itself is untouched by either — there is no pose in it to touch.
  assert(rig_count(r) == 2, "two bones");
  assert((r.rg_len[1] ?? 0.0) == 2.0, "the forearm is still 2 long");
  j = rig_joint(r, 1);
  assert(j.jt_value == 0.0, "and a joint read off the rig has no value of its own — only limits");
  assert(j.jt_lo == 0.0 - 0.25 && j.jt_hi == 0.25, "the limits, got {j.jt_lo}..{j.jt_hi}");
}

// @HXB-002 — THE VALUES VECTOR IS POSITIONAL, UNCHECKED, AND SILENTLY
// DEFAULTED.  Every kinematics call reads `values[k] ?? 0.0`, so a vector that
// is too short poses the missing joints at zero and returns a complete,
// ordinary-looking body.  A value outside the joint's own limits poses too:
// the limits are a DOORSTEP the caller calls, not a guard the pose enforces.
//
// The sharpest case is a value that lands back where it started.  Joint values
// are TURNS, so 9.0 is nine revolutions — outside every limit in this rig, and
// its pose is indistinguishable from the neutral one.  A pose cannot tell you
// its values were wrong.
fn test_the_values_vector_is_positional_and_unchecked() {
  r = arm();
  empty: vector<float> = [];
  (_a, _b, ex, ey) = rig_world_seg(r, empty, 1);
  (_c, _d, zx, zy) = rig_world_seg(r, [0.0, 0.0], 1);
  assert(ex == zx && ey == zy, "an EMPTY vector poses the whole rig neutral, not an error");
  assert(ex == 5.0, "and answers a perfectly ordinary body, got ({ex},{ey})");

  // Nine turns is outside the joint's limits, and it poses anyway.
  j = rig_joint(r, 1);
  assert(!joint_fits(j, 9.0), "9.0 turns does not fit a +-0.25 joint");
  (_e, _f, nx, ny) = rig_world_seg(r, [0.0, 9.0], 1);
  assert(fabs3(nx - 5.0) < 1e-12 && fabs3(ny) < 1e-12,
         "yet its pose is the neutral one to the last ulp, got ({nx},{ny})");

  // The doorstep the caller owes: a refusal names an offer and a residual.
  assert(joint_offer(j, 9.0) == 0.25, "the nearest admissible value is the limit");
  assert(fabs3(joint_residual(j, 9.0) - 8.75) < 1e-12, "and the residual is 8.75 turns");
  assert(joint_fits(j, joint_offer(j, 9.0)), "the offer itself fits");
}

// @HXB-003 — POSE A LIMB THROUGH ONE FRAME, NOT ONE TREE WALK PER POINT.  A
// bone's `Frame` is where its joint sits and how its own axes lie, and every
// point hung on that bone — a mesh, a mount, a muzzle — is that frame applied
// to a local offset.  `rig_world_point3` is the one-shot door and it walks the
// whole tree each time; `rig_world_frame3` walks it once and hands you the
// frame to reuse.
//
// The numbers are identical either way, which is what makes the choice purely
// about cost: `rig_world_seg3` is itself exactly two points of that frame.
fn test_hold_the_frame_not_the_walk() {
  r = arm();
  r = rig_bone3(r, 1, 2.0, 0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0 - 1.0, 1.0);
  vals = [0.13, 0.21, 0.07];
  f = rig_world_frame3(r, vals, 2);

  same = true;
  for k in 0..5 {
    lx = 0.1 * (k as float);
    ly = 0.2 - 0.05 * (k as float);
    lz = 0.03 * (k as float);
    (ax, ay, az) = frame_point(f, lx, ly, lz);
    (bx, by, bz) = rig_world_point3(r, vals, 2, lx, ly, lz);
    if ax != bx || ay != by || az != bz { same = false; }
  }
  assert(same, "five points through one frame are byte-identical to five walks");

  // And the segment is the frame's origin and its tip, nothing more.
  (s0x, s0y, s0z, s1x, s1y, s1z) = rig_world_seg3(r, vals, 2);
  (o0x, o0y, o0z) = frame_point(f, 0.0, 0.0, 0.0);
  (o1x, o1y, o1z) = frame_point(f, r.rg_len[2] ?? 0.0, 0.0, 0.0);
  assert(s0x == o0x && s0y == o0y && s0z == o0z, "the base is the frame's origin");
  assert(s1x == o1x && s1y == o1y && s1z == o1z, "the tip is (len,0,0) in it");
  assert(s0x == f.fr_x && s0y == f.fr_y && s0z == f.fr_z, "which is where the joint sits");
}

// @HXB-004 — THE AXIS IS STORED AS YOU GAVE IT, AND AN ABSENT ONE IS NEVER
// REPAIRED.  A revolute axis carries a direction and no length: `(0,0,2)` and
// `(0,0,1)` are the same hinge, because the walk normalises where it needs a
// unit vector rather than at the door.  For the planar axis that divide is by
// exactly 1.0, which is why a planar bone's numbers are untouched by the 3-D
// path existing at all.
//
// An axis of `(0,0,0)` is a joint with nothing to turn about.  It poses as the
// IDENTITY — the bone simply does not turn, whatever its value — and
// `rig_admissible` refuses the rig.  It is NOT quietly read as `+z`: a rig
// whose axis nobody wrote is refused loudly, the same way a malformed text is.
fn test_the_axis_is_as_given_and_an_absent_one_is_refused() {
  unit = rig_new();
  unit = rig_bone3(unit, -1, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 1.0, 0.0 - 1.0, 1.0);
  twice = rig_new();
  twice = rig_bone3(twice, -1, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 2.0, 0.0 - 1.0, 1.0);
  none = rig_new();
  none = rig_bone3(none, -1, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0 - 1.0, 1.0);
  v = [0.125];

  (_a, _b, _c, ux, uy, uz) = rig_world_seg3(unit, v, 0);
  (_d, _e, _f, tx, ty, tz) = rig_world_seg3(twice, v, 0);
  assert(ux == tx && uy == ty && uz == tz, "a doubled axis is the same hinge, bit for bit");
  assert(fabs3(ux - 1.4142135623730951) < 1e-12, "an eighth turn puts the tip at 2/sqrt(2), got {ux}");

  (_g, _h, _i, nx, ny, nz) = rig_world_seg3(none, v, 0);
  assert(nx == 2.0 && ny == 0.0 && nz == 0.0, "no axis means no turn — the identity, got ({nx},{ny},{nz})");
  assert(!rig_admissible(none), "and the rig is refused rather than repaired to +z");
  assert(rig_admissible(unit) && rig_planar(unit), "while the planar rig is admissible and planar");
  assert(!rig_planar(spatial_arm()), "and a rig hinged about +y is not planar");
}

// @HXB-005 — THE 2-D PATH IS FOR PLANAR RIGS, AND IT SAYS SO WITH NUMBERS
// RATHER THAN A REFUSAL.  `rig_world_seg` composes joints by ADDING ANGLES, so
// it reads neither the stored axis nor `oz`: hand it a rig hinged about `+y`
// and it answers as though that hinge were about `+z`, in the plane.  The rig
// is admissible, the call returns four ordinary floats, and nothing says the
// body it describes is not the body you built.
//
// The collision proxy is built on that same walk, so it inherits the mistake:
// `bone_obb` boxes a bone that is somewhere else, and `bone_shape_has` answers
// for it.  Measured on the arm below, the two tips are 2.5 apart at a quarter
// turn and 3.2 apart at three quarters — on a bone 2 long.
//
// So `rig_planar` is not a curiosity: it is the precondition of the entire 2-D
// half of this library, and a caller with a spatial rig must use
// `rig_world_seg3` / `rig_world_frame3` and derive its own proxy.
fn test_the_two_dimensional_path_is_planar_only() {
  r = spatial_arm();
  assert(rig_admissible(r), "the spatial arm is a perfectly admissible rig");
  assert(!rig_planar(r), "and it is not planar — which is the question to ask first");

  vals = [0.0, 0.25];
  (_a, _b, px, py) = rig_world_seg(r, vals, 1);
  (_c, _d, _e, sx, sy, sz) = rig_world_seg3(r, vals, 1);
  assert(px == 3.0 && py == 2.0, "the 2-D walk puts the tip up the y axis, got ({px},{py})");
  assert(sx == 3.0 && sy == 0.0 && sz == 0.0 - 1.5,
         "the bone is actually below the plane, at ({sx},{sy},{sz})");
  gap = sqrt((px - sx) * (px - sx) + (py - sy) * (py - sy) + sz * sz) ?? 0.0;
  assert(fabs3(gap - 2.5) < 1e-12, "2.5 apart on a bone 2 long, got {gap}");

  // The worst of it, over a full turn of that joint.
  worst = 0.0;
  for k in 0..9 {
    v = (k as float) * 0.125;
    (_f, _g, qx, qy) = rig_world_seg(r, [0.0, v], 1);
    (_h, _i, _j, ax, ay, az) = rig_world_seg3(r, [0.0, v], 1);
    d = sqrt((qx - ax) * (qx - ax) + (qy - ay) * (qy - ay) + az * az) ?? 0.0;
    if d > worst { worst = d; }
  }
  assert(fabs3(worst - 3.2015621187164243) < 1e-9, "worst separation 3.20, got {worst}");
  assert(worst > (r.rg_len[1] ?? 0.0), "further apart than the bone is long");

  // …and the proxy that a consumer would collide against is built on the 2-D walk.
  o = bone_obb(r, vals, 1, W);
  assert(fabs3(o.ob_cx - 3.0) < 1e-12 && fabs3(o.ob_cy - 1.0) < 1e-12,
         "the box is centred on the 2-D segment, got ({o.ob_cx},{o.ob_cy})");
  assert(!obb_contains(o, sx, sz), "so it does not contain the bone's real position in its own plane");

  // The planar arm, by contrast, is exactly what both walks describe.
  p = arm();
  (_k, _l, ppx, ppy) = rig_world_seg(p, vals, 1);
  (_m, _n, _o2, qsx, qsy, qsz) = rig_world_seg3(p, vals, 1);
  assert(qsz == 0.0 && fabs3(ppx - qsx) < 1e-12 && fabs3(ppy - qsy) < 1e-12,
         "a planar rig agrees on both paths");
}

// @HXB-006 — A PROXY CONTAINS ITS SHAPE, AND ITS OVERSHOOT IS EXACTLY STATED.
// The collision proxy is DERIVED from the geometry rather than hand-authored: a
// bone's shape is the capsule of half-width `w` around its posed segment, and
// its proxy is the oriented box that bounds that capsule.  Containment is the
// half that must never fail — a shape point outside the proxy is a MISSED
// overlap — and the price of it is overshoot, which is bounded and worth
// knowing exactly: `w * (sqrt(2) - 1)` at the four corners, and nothing
// anywhere else.
fn test_a_proxy_contains_its_shape_with_a_stated_overshoot() {
  r = arm();
  vals = [0.0, 0.0];
  o = bone_obb(r, vals, 0, W);
  (x0, y0, x1, y1) = rig_world_seg(r, vals, 0);
  assert(fabs3(o.ob_hl - (1.5 + W)) < 1e-12, "the box grows by w at each end for the caps");
  assert(o.ob_hw == W, "and is w wide");

  nshape = 0;
  missed = 0;
  worst = 0.0;
  for gi in 0..161 {
    for gj in 0..81 {
      gx = 0.0 - 1.0 + (gi as float) * 0.03125;
      gy = 0.0 - 1.25 + (gj as float) * 0.03125;
      d = seg_dist(x0, y0, x1, y1, gx, gy);
      inside = obb_contains(o, gx, gy);
      if d <= W {
        nshape = nshape + 1;
        if !inside { missed = missed + 1; }
      }
      if inside && d - W > worst { worst = d - W; }
    }
  }
  assert(nshape > 3000, "the grid really covers the capsule, {nshape} points");
  assert(missed == 0, "and NOT ONE of them is outside the proxy");
  assert(worst <= W * 0.41421356237309515 + 1e-9,
         "the overshoot never exceeds w*(sqrt2-1), got {worst}");
  assert(worst > W * 0.41 - 1e-9, "and reaches it at the corners — the bound is tight, got {worst}");
  assert(bone_shape_has(r, vals, 0, W, 1.5, 0.4), "a point in the capsule is in the shape");
  assert(!bone_shape_has(r, vals, 0, W, 1.5, 0.6), "one beyond w is not");
}

// @HXB-007 — A WHEEL'S ANGLE IS COMPUTED FROM ITS TRAVEL, WHICH IS WHY IT DOES
// NOT SLIP.  The rolling identity `r * theta = distance` holds here BY
// CONSTRUCTION, not by tuning: the joint value is a function of how far the
// wheel has gone, so there is no place for an error to accumulate.  What is
// left is one float round trip — a divide by `2*pi*r` and a multiply back — so
// the residual skid is machine epsilon, not algebraic zero, and this is the
// one place in the body where imprecision is licensed at all.
//
// The contrast is the whole point, and it grows with the journey: adding a
// per-step increment instead drifts, and by 20000 steps it is thousands of
// times further off than the derived value ever gets.
fn test_a_wheel_rolls_by_construction_not_by_tuning() {
  radius = 2.5;
  step = 0.37;
  worst_derived = 0.0;
  worst_acc = 0.0;
  acc = 0.0;
  for k in 0..20001 {
    travel = (k as float) * step;
    sk = wheel_skid(radius, wheel_value(travel, radius, 0.0), travel);
    if sk > worst_derived { worst_derived = sk; }
    if k > 0 { acc = acc + wheel_value(step, radius, 0.0); }
    ska = wheel_skid(radius, acc, travel);
    if ska > worst_acc { worst_acc = ska; }
  }
  assert(worst_derived < 1e-11, "7400 units of travel, derived: still machine dust ({worst_derived})");
  assert(worst_acc > worst_derived * 1000.0,
         "the same journey accumulated is over a thousand times worse ({worst_acc})");

  // The control that the measurement can fail at all: ask for a skid.
  slipped = wheel_skid(radius, wheel_value(10.0, radius, 0.25), 10.0);
  assert(slipped > 2.0, "a quarter of deliberate slip is a real distance, got {slipped}");
  assert(fabs3(wheel_angle(1.0) - 6.283185307179586) < 1e-12, "a value of 1.0 is one whole turn");
}

// @HXB-008 — INDEX ORDER IS THE RIG'S CANONICAL LABELLING, AND A SPATIAL BONE
// CHANGES ITS RECORD TAG.  A tree can be relabelled and describe the same rig,
// so the text fixes the labels: the root is bone 0 and every bone's parent is
// an EARLIER bone.  The parser enforces it — a rig written out of order is
// REFUSED, not sorted — which is what makes `write(read(R))` a byte identity.
//
// The tag is the compatibility half.  A planar bone writes the bytes this
// library has always written, so a planar rig round-trips across versions
// unchanged; a spatial bone writes `bone3`, so a reader that predates the third
// dimension REFUSES the rig instead of silently reading its planar projection —
// which is the same choice as refusing an absent axis rather than assuming one.
fn test_index_order_is_the_labelling_and_a_spatial_bone_says_so() {
  p = rig_new();
  p = rig_bone(p, -1, 0.0, 0.0, 1.5, 0.0 - 0.5, 0.5);
  assert(rig_write(p, "arm") == "rig arm bones 1\nbone 0 parent -1 at 0 0 len 1.5 lim -0.5 0.5",
         "the planar record, byte for byte: [{rig_write(p, \"arm\")}]");

  s = rig_new();
  s = rig_bone3(s, -1, 0.0, 0.0, 0.25, 1.5, 1.0, 0.0, 0.0, 0.0 - 0.5, 0.5);
  assert(rig_write(s, "arm") == "rig arm bones 1\nbone3 0 parent -1 at 0 0 0.25 len 1.5 axis 1 0 0 lim -0.5 0.5",
         "and the spatial one carries its own tag: [{rig_write(s, \"arm\")}]");

  assert(rig_eq(p, rig_read(rig_write(p, "arm"))), "both round-trip through their own text");
  assert(rig_eq(s, rig_read(rig_write(s, "arm"))), "floats included");

  // Out of topological order: refused, and a refusal is an EMPTY rig.
  bad = "rig arm bones 2\nbone 1 parent -1 at 0 0 len 1 lim 0 0\nbone 0 parent 0 at 0 0 len 1 lim 0 0";
  assert(rig_count(rig_read(bad)) == 0, "a rig whose bones are out of order is refused");
  assert(rig_count(rig_read(rig_write(p, "arm"))) == 1, "while the writer's own text still reads");

  // A float spelled any way but the writer's is refused too — the reader takes
  // exactly what the writer emits or the byte diff stops meaning anything.
  for spelling in ["1.0", "+1.5", "01.5", "1.50", "1.5e0", "x"] {
    t = "rig arm bones 1\nbone 0 parent -1 at 0 0 len {spelling} lim -0.5 0.5";
    assert(rig_count(rig_read(t)) == 0, "`len {spelling}` is not how this library spells 1.5");
  }
  assert(rig_count(rig_read("rig arm bones 1\nbone 0 parent -1 at 0 0 len 1.5 lim -0.5 0.5 ")) == 0,
         "nor is a trailing space");

  // …but the PARENT is not the reader's business: a forward parent round-trips
  // faithfully, so the reader takes it and the DOORSTEP is what refuses it.
  forward = "rig arm bones 2\nbone 0 parent 1 at 0 0 len 1 lim 0 0\nbone 1 parent -1 at 3 0 len 2 lim 0 0";
  fr = rig_read(forward);
  assert(rig_count(fr) == 2, "a forward parent READS — nothing about it is misspelled");
  assert(rig_write(fr, "arm") == forward, "and it re-writes byte for byte");
  assert(!rig_admissible(fr), "while the doorstep refuses it: a parent must be an EARLIER bone");

  // Which is why the doorstep is not optional.  Posed anyway, bone 0 reads its
  // parent's frame before that parent exists, and gets zeros — a body at the
  // origin, with no complaint.
  (_g, _h, gx, gy) = rig_world_seg(fr, [0.0, 0.0], 0);
  assert(gx == 1.0 && gy == 0.0, "it poses from the origin, got ({gx},{gy})");
}