The long-jaw Fin-Ray blade, rebuilt from operator field-test feedback. Mesh checks all pass and the root failure that killed v3 is fixed. One dimension is still blank on purpose — nobody has measured it yet, and guessing it would be worse than leaving it at zero.
Re-verified 2026-08-21: connected shells = 1, watertight, manifold,
consistently wound, zero degenerate faces, signed volume 29,424 mm³.
check_ribs.py on the toothless body gives rib pitch 4.06 mm,
compliance 0.8×, outlier 1.59×, largest cavity 60.7 mm² — all pass.
1. HOLE_DX = 0. The mounting holes should shift inward laterally, but
by how much has never been measured. The field-test doc says explicitly not to invent a
number, so it sits at zero until someone measures it. Everything else about the part is final.
2. Jaw opening with the sleeve fitted. The TPU sleeve adds 9.34 mm past the grip
face on each blade, so a closed pair loses 18.68 mm of opening. Whether a
90 mm cup still fits has not been confirmed. gripper_range.py answers it,
but that script drives the real gripper motor — it cannot be settled from files.
These are rendered directly from Blade_v4_final.stl, not from the source
script — so what you see is what prints.
v3 built texture by sweeping the body along X, subtracting to get a skin, then intersecting with a rib array. That path produced 40 loose shells, then 3, then two zero-area triangles, then 4 non-manifold edges — and finally the plane it depended on turned out to cut straight through the grip face itself. The whole boolean-skin mechanism is deleted; texture is generated parametrically in the body now.
The blade inherits Ø3.39 holes at z=−30.485. v3 plugged them at the new hole height −31.735 — 1.25 mm off, so an r=2.2 plug missed the top rim of an r=1.695 hole and left a 3.37 × 1.55 crescent gap in section. Plugs now use the old holes' own height.
27.70 mm of continuous material at z=−26, up from 7.47 mm before the fix. This is the load path from the ribs into the mount; v3 was hollow where it needed to be solid.
Both were hit on 2026-08-21. A texture band can be silently clipped away while the script
still "has" it — caught by comparing vertex counts per 5 mm z-band, body vs toothed.
And a hairline gap passes watertightness — caught by slicing at y=0 and listing every
closed loop under 6×6 mm. check_mesh.py alone is not sufficient.
| Item | Value | Status |
|---|---|---|
| Connected shells | 1 | ✓ pass |
| Watertight / manifold / winding / slivers | — | ✓ all pass |
| Signed volume | 29,424 mm³ | ✓ pass |
| Rib pitch | 4.06 mm | ✓ pass |
| Compliance | 0.8× | ✓ pass |
| Largest cavity | 60.7 mm² | ✓ pass |
| Mounting holes | Ø2.80 × 2 | ✓ z=−31.737, 10.0 mm apart |
| Hole edge margin | 0.850 mm | ✓ at the TPU floor |
| Old holes plugged | — | ✓ no residue in section |
| y=0 small closed loops | 3 | ✓ 2 holes + 1 design cavity |
| Tip thickness @ z=55.2 | 1.13 mm | ✓ sharp |
| Root solid @ z=−26 | 27.70 mm | ✓ was 7.47 mm |
Lateral hole offset HOLE_DX | 0 | ! unmeasured |
| Jaw opening with sleeve | −18.68 mm | ! unverified vs 90 mm cup |
HOLE_DX, then rebuild
Hold the two blades inner-face to inner-face, measure the outside-to-outside span across the
feet, and compare against the original part keeping the centre fixed — that back-solves the
hole position. Put the number into HOLE_DX in
generate_v4_finish.scad and re-run the third build step. Nothing else changes.
| Step | Command |
|---|---|
| 1 · body, no teeth | openscad -D TEETH=false -o Blade_v4_body.stl generate_blade_arcsweep.scad |
| 2 · body with teeth | openscad -D TEETH=true -o Blade_v4_teeth.stl generate_blade_arcsweep.scad |
| 3 · drill → final | openscad -o Blade_v4_final.stl generate_v4_finish.scad |
Order matters. check_ribs.py runs on the toothless body, not the final part
— running it on the finished mesh reads the teeth as rib geometry and reports nonsense.