Voice-driven conversational 3D woodworking & furniture modeler
Go to file
rob 8019aac299 Miter: adjustable hinge offset instead of a center toggle
Replaces the from_center boolean with miter_offset_in / bevel_offset_in: how far
to move the cut's hinge IN from the edge. 0 = full edge-to-edge cut; half the
board size = the centre (corner notch); two centred cuts (+angle/−angle) make a
point (picket); intermediate/over values give asymmetric & partial cuts — much
more range than the old edge/centre toggle.

- Feature.miter_offset_in/bevel_offset_in (replaces from_center); geometry pivot
  = edge + inward·offset (per width/thickness); serialization additive.
- Joinery panel: Miter offset / Bevel offset spin fields (miter only), checkbox
  removed. CLI feature --miter-offset/--bevel-offset; wood-feature tool args
  (regenerated); controller passthrough; apply_preview carries the offsets.
- tests updated: offset=half-width notches a corner, two make a point, offset
  roundtrips. 247 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:11:19 -03:00
scripts Miter: adjustable hinge offset instead of a center toggle 2026-05-31 10:11:19 -03:00
src/woodshop Miter: adjustable hinge offset instead of a center toggle 2026-05-31 10:11:19 -03:00
tests Miter: adjustable hinge offset instead of a center toggle 2026-05-31 10:11:19 -03:00
.gitignore Initial project setup 2026-05-29 00:59:35 -03:00
CLAUDE.md Portability + consistency polish (Codex review) 2026-05-30 21:56:28 -03:00
MATERIALS_INVENTORY_PLAN.md Material-aware pricing: oak ≠ pine 2026-05-30 19:48:26 -03:00
README.md Multi-image reference + render-feedback "Match photo" self-correction 2026-05-30 23:25:13 -03:00
SHOP_PACKET_PLAN.md Phase 1: bounded-exact lumber, guillotine plywood, Best-of-N 2026-05-30 15:37:11 -03:00
pyproject.toml Portability + consistency polish (Codex review) 2026-05-30 21:56:28 -03:00

README.md

WoodShop

Voice-driven conversational 3D woodworking & furniture modeler.

Talk to it like the Star Trek holodeck and watch furniture build itself:

"Place a 6 foot 2x4, sand it, then attach a 2 foot 2x4 at 90 degrees, 10 inches from the end."

"Build a coffee table: a four foot by two foot frame from 2x4s, with four legs 18 inches tall standing at the corners."

You can also attach reference(s) (📎, drag-drop, paste, or a URL) and say "build something like this": one or several photos (front/side/detail), a PDF plan, a 3D model (STL/STEP/OBJ — rendered to an image, with its bounding box measured), or a web-page guide (its text is pulled). WoodShop builds a simplified, buildable interpretation in dimensional lumber. Then click 🔄 Match photo and it renders the build from several angles, compares them to your reference, and self-corrects — repeat until it looks right. (Still an interpretation, not a measured replica.)

Each board is real dimensional lumber (a 2x4 is modeled at its true 1.5″ × 3.5″), so the result is buildable — export to STEP (CAD/CNC) or STL (3D print), and get a cut list with board-feet and a shopping estimate.

How it works

WoodShop reuses the existing CmdForge tool ecosystem for everything that isn't woodworking-specific, so no wheels are reinvented:

woodshop-talk ── the conversational loop
   │  dictate ............. speech → text          (CmdForge tool)
   │  pa-load-tools ....... wood-* → Claude schemas (CmdForge tool)
   │  claude -p ........... interpret → tool calls  (provider)
   │  pa-execute-tool ..... dispatch each wood-*     (CmdForge tool)
   │  read-aloud .......... speak confirmation       (CmdForge tool)
   ▼
scene.json  ← single source of truth (parts, joints, selection, undo)
   ▲                                   │ writes
   │ reads/mutates                     ▼
wood-* CmdForge tools          woodshop-view
(place/join/stand/move/...)    live pyvista 3D, watches scene.json

The wood-* tools are thin wrappers over the woodshop CLI, so the modeling logic lives in one place and the tools double as the LLM's documented command vocabulary.

Installation

python -m venv .venv && source .venv/bin/activate
pip install -e ".[gui,dev]"        # 'gui' pulls build123d + pyvista + PySide6 + pyvistaqt
python scripts/gen_wood_tools.py   # register the wood-* CmdForge tools

Usage

woodshop            # launches the unified desktop app

One window with the 3D viewport (click a board to select it; Ctrl+click to select several), a parts panel (list + selected-part inspector + quick-action buttons), a numberpad control panel (move/rotate the selection by clicking or with your keyboard's numpad — 2/4/6/8 move, 1/3/7/9 rotate, +/ raise/lower, 0/. front/iso, 5 fit), and a command bar where you type or push-to-talk (🎤). Mouse, keyboard, and voice all drive the same scene and the same visible selection — so "move these 4 inches", the numpad 8 key, and the move button are interchangeable, and act on every selected board at once (one undo). Menus cover New/Open/Save projects, Export STL/STEP, Save Image, Undo/Redo, camera views, and Build templates.

Standalone tools (headless / scripting)

woodshop-view &                    # just the live 3D window (watches the scene)
woodshop-talk                      # just the voice/text loop; --voice to speak
woodshop-talk --once "build a workbench top from five 2x6 boards 6 feet long"

Or drive it directly from the CLI:

woodshop place 2x4 "6 ft"          # place a board
woodshop stand                     # stand it up (a leg)
woodshop join p2 --to p1 --angle 90 --offset "10 in"
woodshop rename "front-left leg"
woodshop cutlist                   # bill of materials
woodshop export table.step         # STEP / STL export
woodshop save "coffee table"       # named projects
woodshop open "coffee table"

Run woodshop --help for the full command list (place, join, stand, lay, rotate, move, trim, copy, rename, sand, delete, undo, clear, status, cutlist, export, save, open, projects).

The active scene lives at $WOODSHOP_SCENE or ~/.local/share/woodshop/scene.json; named projects in ~/.local/share/woodshop/projects/.

Development

pytest                              # 200+ tests

Key modules:

Module Role
scene.py Part/Joint/Connection/Feature/Scene model, ops, undo, persistence
lumber.py nominal → actual dimensions, material colors/defaults
colors.py color name → hex + lightness blends for the viewer
units.py parse "6 ft" / "3 ft 6 in" / "-2 ft" → inches
cli.py the woodshop command
geometry.py build123d solids (incl. joinery booleans) + STL/STEP export
cutlist.py quick cut list / board-feet / shopping summary
cutplan.py the deterministic keystone: kerf-aware nesting, rough/final, batch, offcut reuse
prices.py editable price book (Kent NB) + material-aware cost estimate
estimate.py project quote: consumables + labor + suggested selling price
inventory.py shop-wide event-sourced stock / offcut / build ledger
instructions.py / jigs.py deterministic build steps & jig suggestions
viewer.py live pyvista 3D viewport (woodshop-view)
driver.py conversational loop (woodshop-talk)
gui/ the unified PySide6 studio (woodshop-gui), incl. the Cut List & BOM window
scripts/gen_wood_tools.py (re)generate the wood-* CmdForge tools

Known limitations

  • Joinery is parametric (tenon/mortise/dado/rabbet/hole/slot/chamfer as build123d boolean ops, with connections/assemblies); what's not modeled is joinery-fit compensation for material lost to sanding, and lap/pocket-hole presets. Boards still attach as flush butt joints unless you add features.
  • Render is flat colors per material/finish — no image textures (wood grain) yet.
  • Command interpretation latency is ~713s per utterance (one claude -p call).

License

MIT