hex_body API

← hex_body · version 0.3.6

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

pub struct Joint { jt_value: float, jt_lo: float, jt_hi: float }

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 fn joint_new(value: float, lo: float, hi: float) -> Joint

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 pose_of(value: float, lx: float, ly: float) -> (float, float)

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.

pub fn wheel_value(travel: float, radius: float, slip: float) -> float

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.

pub fn wheel_angle(value: float) -> float

The joint value as an ANGLE in radians.

pub fn wheel_skid(radius: float, value: float, travel: float) -> float

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 ε.

pub fn joint_fits(j: Joint, value: float) -> boolean

THE DOORSTEP (K-FIT for a joint): a value inside the joint's limits fits.

pub fn joint_offer(j: Joint, value: float) -> float

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.

pub fn joint_residual(j: Joint, value: float) -> float

The residual an offer carries — how far the requested value was from admissible.

pub struct Rig { rg_parent: vector<integer>, rg_ox: vector<float>, rg_oy: vector<float>, rg_oz: vector<float>, rg_len: vector<float>, rg_ax: vector<float>, rg_ay: vector<float>, rg_az: vector<float>, rg_lo: vector<float>, rg_hi: vector<float> }

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 fn rig_new() -> Rig

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_bone3(r: Rig, parent: integer, ox: float, oy: float, oz: float, len: float, ax: float, ay: float, az: float, lo: float, hi: float) -> Rig

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.

pub fn rig_bone(r: Rig, parent: integer, ox: float, oy: float, len: float, lo: float, hi: float) -> Rig

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.

pub fn rig_count(r: Rig) -> integer

How many bones the rig holds — the bound for any walk over it.

pub fn bone_planar(r: Rig, i: integer) -> boolean

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 rig_planar(r: Rig) -> boolean

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.

pub fn rig_joint(r: Rig, i: integer) -> Joint

The joint at bone i, as a Joint carrying its limits (value defaults to 0 — the neutral pose).

pub fn rig_admissible(r: Rig) -> boolean

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.

pub fn rig_write(r: Rig, name: text) -> text

── 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.

pub fn rig_read(t: text) -> Rig

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.

pub fn rig_eq(a: Rig, b: Rig) -> boolean

Byte-exact equality — floats compared with ==, no tolerance (I-EXACT).

pub fn rig_world_seg(r: Rig, values: vector<float>, i: integer) -> (float, float, float, float)

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.

pub struct Frame { fr_x: float, fr_y: float, fr_z: float, fr_00: float, fr_01: float, fr_02: float, fr_10: float, fr_11: float, fr_12: float, fr_20: float, fr_21: float, fr_22: float }

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 fn rig_world_frame3(r: Rig, values: vector<float>, i: integer) -> Frame

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.

pub fn frame_point(f: Frame, lx: float, ly: float, lz: float) -> (float, float, float)

Where a point in the bone's LOCAL frame lands in the world.

pub fn rig_world_point3(r: Rig, values: vector<float>, i: integer, lx: float, ly: float, lz: float) -> (float, float, float)

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.

pub fn rig_world_seg3(r: Rig, values: vector<float>, i: integer) -> (float, float, float, float, float, float)

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.

pub struct Obb { ob_cx: float, ob_cy: float, ob_ux: float, ob_uy: float, ob_hl: float, ob_hw: float }

An oriented bounding box: centre, unit axis (along the bone), and half-extents.

pub fn bone_obb(r: Rig, values: vector<float>, i: integer, w: float) -> Obb

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.

pub fn obb_contains(o: Obb, px: float, py: float) -> boolean

Is a world point inside the OBB? Project onto the axis and its perpendicular.

pub fn seg_dist(x0: float, y0: float, x1: float, y1: float, px: float, py: float) -> float

Distance from a point to a segment — the capsule test's core.

pub fn bone_shape_has(r: Rig, values: vector<float>, i: integer, w: float, px: float, py: float) -> boolean

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.