- Python 55.3%
- OpenSCAD 44.7%
| docs | ||
| .gitignore | ||
| CLAUDE.md | ||
| gen_fontlist.py | ||
| parametric_sign.scad | ||
| README.md | ||
| sign.py | ||
Parametric Sign
A parametric 3D-printable sign: a rounded-rectangle base plate with an inset raised border ring on top, up to three lines of raised text, and optional holes for hanging it on a wall.
(Default sign: 200 × 60 mm, 25% rounding, "OPEN" / "9 AM - 5 PM" in Martian Mono, no hanging holes.)
Two independent implementations
The same object, built two different ways — pick whichever fits your workflow. There's no shared code between them; geometry changes have to be ported to both by hand.
sign.py |
parametric_sign.scad |
|
|---|---|---|
| Tool | Python + build123d | OpenSCAD (Customizer-annotated) |
| Best for | Scripting, CLI-driven generation, batch exports | Interactive tweaking in the OpenSCAD GUI |
| Base plate + border | ✅ | ✅ |
| Up to 3 lines of raised text | ✅ | ✅ |
Through-hole hanging holes (--holes) |
✅ | ✅ |
Keyhole hanging slots (--hang) |
✅ | ❌ (intentional — see below) |
| Interactive prompt mode | ✅ (--interactive) |
n/a (use the GUI Customizer) |
Keyhole slots are Python-only, on purpose. They were never ported to the
.scadfile — the sign is normally hung with a 3M Command strip, so the recessed keyhole feature is a nice-to-have that only exists on the scriptable side. Don't "fix" this without checkingCLAUDE.mdfirst.
Quick start
Python (sign.py)
Self-contained uv script — dependencies are declared inline, no venv setup
needed.
uv run sign.py # defaults -> sign.stl
uv run sign.py -w 80 -H 50 -r 100 -o pill.stl
uv run sign.py -v # print derived dimensions, then build
uv run sign.py --hang single -v # one centered keyhole slot on the back
uv run sign.py --holes double # two mirrored through-holes near the top
uv run sign.py --holes single --hole-dia 4
uv run sign.py --line1 OPEN --line2 "9 AM - 5 PM" # raised text (up to 3 lines)
uv run sign.py --interactive # prompt for any args not given
OpenSCAD (parametric_sign.scad)
openscad -o sign.stl parametric_sign.scad # headless STL export
openscad -D 'hole_mode="double"' -o sign.stl parametric_sign.scad # with through-holes
openscad parametric_sign.scad # open GUI / Customizer
Font choices in the Customizer's font_family dropdown are auto-generated
from your installed system fonts:
uv run gen_fontlist.py # rewrites the font_family // [...] option list
Geometry model
All units are millimeters. Rounding is a percentage of the maximum possible corner radius, not an absolute value:
corner_r = (min(width, height) / 2) * rounding_pct / 100
border_outer_r = corner_r - border_inset
border_inner_r = corner_r - border_inset - border_width
So 0% gives sharp corners and 100% gives fully rounded, semicircular short
ends. The raised border is concentric with the base plate — same center,
radii stepped inward by border_inset and border_width.
Both implementations validate the spec up front and fail loudly on invalid combinations (border doesn't fit inside the corner radius, hanging holes run off an edge, etc.) rather than emitting broken geometry.
Text
Raised text on the face, up to three lines, each extruded flush with the top of the border. Only visible (enabled + non-blank) lines are drawn, stacked top-to-bottom and centered as a block — hide a line and the rest re-center.
Fonts are resolved differently by each engine: the .scad looks up
fontconfig family names (falls back silently on an unknown one), while
sign.py resolves an actual font file path and fails loudly if it can't find
one. See CLAUDE.md for the details if you're adding a new font.
Hanging
Two independent, combinable ways to hang a sign:
- Through-holes (
--holes/hole_mode, both implementations) — plain cylindrical holes punched straight through near the top, for a nail or screw.singlecenters one hole;doublemirrors two about the vertical centerline. - Keyhole slots (
--hang, Python only) — recessed slots on the back that let a screw head slide up and lock behind a narrower lip, so nothing shows from the front.
Development
No tests, linters, or build config — uv run sign.py is the full dev loop.
See CLAUDE.md for the full geometry/validation contract shared across both
implementations.
