Verify the enclosure STLs and renders against the model; document how to regenerate them

Re-exported all ten STLs and the eight poster pose parts with OpenSCAD 2021.01: same
triangles as the committed files. Recovered the camera for renders/assembly.png and
exploded.png (pixel-identical apart from a few anti-aliased pixels) and wrote the
recipes into enclosure/README.md. The poster script now finds DejaVu fonts on more
distros (or via POSTER_FONT_DIR) and the poster README's pose_info command works on 2021.01.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
2026-10-05 18:52:34 -07:00
co-authored by Claude Sonnet 5.5
parent 720a7cfc34
commit cb2bb28cd4
3 changed files with 42 additions and 6 deletions
+19
View File
@@ -44,6 +44,25 @@ Every value marked `MEASURE` in the `.scad` file is an estimate. With calipers,
Change a value, then re-export: `openscad -D 'PART="body"' -o stl/body.stl hermes_robot.scad`. Change a value, then re-export: `openscad -D 'PART="body"' -o stl/body.stl hermes_robot.scad`.
### Regenerating the STLs and the renders
Checked 2026-10-05 with OpenSCAD 2021.01: every file in `stl/`, `poster/pose/`, `renders/` and the
poster matches the model (same triangles; the two PNGs differ in a handful of anti-aliased pixels).
Without installing anything, via Docker:
```bash
O="docker run --rm --user $(id -u):$(id -g) -v $PWD:/src -w /src openscad/openscad:2021.01"
$O openscad -D 'PART="body"' -o stl/body.stl hermes_robot.scad # any PART
# PNGs need a virtual display with GLX and software GL; without these flags it hangs
R="docker run --rm --user $(id -u):$(id -g) -v $PWD:/src -w /src -e LIBGL_ALWAYS_SOFTWARE=1 openscad/openscad:2021.01 \
xvfb-run -a -s '-screen 0 1280x1024x24 +extension GLX' openscad --imgsize=1100,1500 --viewall --autocenter --colorscheme=Tomorrow"
$R -o renders/assembly.png --camera=0,0,0,72,0,-30,500 -D 'PART="assembly"' hermes_robot.scad
$R -o renders/exploded.png --imgsize=1100,1700 --camera=0,0,0,70,0,150,500 -D 'PART="exploded"' hermes_robot.scad
```
Known quirk: `back.stl` has two non-manifold edges (four faces meet at one corner, near the
back's rear screw cup); slicers repair it silently.
## Print the test pieces first ## Print the test pieces first
| Piece | What it checks | Rough time | | Piece | What it checks | Rough time |
+3 -2
View File
@@ -11,10 +11,11 @@ cd enclosure
for n in base servo_pan plate body yoke servo_tilt cradle camera; do for n in base servo_pan plate body yoke servo_tilt cradle camera; do
openscad -o poster/pose/$n.stl -D "PART=\"pose_$n\"" -D '$fn=48' hermes_robot.scad openscad -o poster/pose/$n.stl -D "PART=\"pose_$n\"" -D '$fn=48' hermes_robot.scad
done done
openscad -o /dev/null -D 'PART="pose_info"' hermes_robot.scad # prints WIN / LED coordinates openscad -o /tmp/pose_info.stl -D 'PART="pose_info"' hermes_robot.scad # prints WIN / LED coordinates
python3 poster/render_poster.py python3 poster/render_poster.py
``` ```
If the window or LED positions change, copy the printed `WIN` and `LED` values into If the window or LED positions change, copy the printed `WIN` and `LED` values into
`render_poster.py`. `face.png` is a frame captured from `docs/face-prototype.html` (flat shape, `render_poster.py`. The script needs the DejaVu fonts (Debian `fonts-dejavu`, Arch `ttf-dejavu`); elsewhere, set
`POSTER_FONT_DIR` to a folder holding them. `face.png` is a frame captured from `docs/face-prototype.html` (flat shape,
P7, speaking). Requires `numpy`, `trimesh` and `Pillow`. P7, speaking). Requires `numpy`, `trimesh` and `Pillow`.
+20 -4
View File
@@ -32,10 +32,26 @@ AMBER = np.array([1.00, 0.66, 0.24]) # phosphor / sodium light
WARM = np.array([0.95, 0.86, 0.70]) # edge highlight WARM = np.array([0.95, 0.86, 0.70]) # edge highlight
TEAL = np.array([0.30, 0.66, 0.70]) # contours, grid TEAL = np.array([0.30, 0.66, 0.70]) # contours, grid
SCREEN = np.array([0.66, 0.82, 1.00]) # P7 phosphor: blue-white SCREEN = np.array([0.66, 0.82, 1.00]) # P7 phosphor: blue-white
FONT_B = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
FONT_L = "/usr/share/fonts/truetype/dejavu/DejaVuSans-ExtraLight.ttf"
FONT_C = "/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed.ttf" def find_font(name):
FONT_M = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" """A DejaVu font by file name. Looks in $POSTER_FONT_DIR first, then where Debian/Ubuntu, Arch and
Fedora keep it, so the poster renders on any of them; fails with a clear message otherwise."""
import os
dirs = [os.environ.get("POSTER_FONT_DIR", ""), "/usr/share/fonts/truetype/dejavu", "/usr/share/fonts/TTF",
"/usr/share/fonts/dejavu", "/usr/share/fonts/dejavu-sans-fonts", "/usr/share/fonts/dejavu-sans-mono-fonts",
os.path.expanduser("~/.local/share/fonts"), os.path.expanduser("~/.fonts")]
for d in dirs:
if d and os.path.isfile(os.path.join(d, name)):
return os.path.join(d, name)
sys.exit(f"render_poster.py needs the DejaVu font {name}. Install DejaVu (Debian: fonts-dejavu, "
f"Arch: ttf-dejavu) or put dejavu-fonts-ttf-2.37/ttf/ somewhere and set POSTER_FONT_DIR to it.")
FONT_B = find_font("DejaVuSans-Bold.ttf")
FONT_L = find_font("DejaVuSans-ExtraLight.ttf")
FONT_C = find_font("DejaVuSansCondensed.ttf")
FONT_M = find_font("DejaVuSansMono.ttf")
# ------------------------------------------------------------------ camera # ------------------------------------------------------------------ camera
target = np.array([0.0, 0.0, 112.0]) target = np.array([0.0, 0.0, 112.0])