feat(skills): OpenSCAD-Skill hinzufuegen

This commit is contained in:
2026-07-15 14:30:52 +02:00
parent 316b779b06
commit c6bbebea04
16 changed files with 4271 additions and 0 deletions
+917
View File
@@ -0,0 +1,917 @@
---
disable-model-invocation: true
name: openscad
description: >
Programmatic 3D CAD with OpenSCAD. Generate .scad files, render STL for 3D printing,
preview as PNG with AI vision feedback. Triggers on: 3D model, STL, 3D print, parametric
design, openscad, CAD, enclosure, bracket, or any 3D modeling task.
argument-hint: "<description of object to design or path to existing .scad file>"
allowed-tools: "Bash(*),Read,Edit,Write,Glob,Grep,Agent"
metadata:
version: 1.0.0
category: 3d-cad
tags: [openscad, 3d-printing, cad, parametric, stl, modeling, design]
---
# OpenSCAD Skill
Design, render, preview, and export 3D models using OpenSCAD's programmatic CAD engine. Supports iterative AI-driven design refinement via rendered PNG analysis.
## Environment
- **OpenSCAD binary**: `/opt/homebrew/bin/openscad` (v2021.01)
- **Working directory for designs**: `~/openscad-projects/` (create per-project subdirectories)
- **Skill scripts**: `~/.claude/skills/openscad/scripts/`
- **Templates**: `~/.claude/skills/openscad/templates/`
- **Language reference**: `~/.claude/skills/openscad/references/`
## Modes
The skill operates in six modes, auto-detected from the user's request:
- **Design** — Create a new 3D model from a description
- **Replicate** — Reproduce a physical object from reference images
- **Reconstruct** — Reverse-engineer an STL mesh into parametric OpenSCAD code
- **Refine** — Iterate on an existing .scad file (modify, preview, repeat)
- **Export** — Render final STL/3MF for 3D printing
- **Analyze** — Review an existing design for printability or improvements
---
## Workflow: Design Mode
When the user asks to create a new 3D object:
### Step 1: Understand Requirements
Clarify with the user:
- **What** is the object? (enclosure, bracket, gear, container, etc.)
- **Dimensions** — key measurements in mm
- **Purpose** — functional print, aesthetic, mechanical fit?
- **Constraints** — printer bed size, material, wall thickness preferences
- **Parametric?** — which dimensions should be adjustable?
### Step 2: Set Up Project
```bash
bash ~/.claude/skills/openscad/scripts/openscad-project.sh init "<project-name>"
```
This creates `~/openscad-projects/<project-name>/` with subdirectories for source, output, and previews.
### Step 3: Generate the .scad File
Write the OpenSCAD code to `~/openscad-projects/<project-name>/src/main.scad`.
**Mandatory file structure (Feature Tree pattern):**
```openscad
// 1. PARAMETERS (independent variables)
width = 60; height = 30; wall = 2;
// 2. DERIVED DIMENSIONS (calculated from parameters)
inner_width = width - 2 * wall;
// 3. BASE PROFILE (2D sketch — the core shape)
module sketch_base() {
offset(r = corner_r)
square([width - 2*corner_r, depth - 2*corner_r], center=true);
}
// 4. PRIMARY BODY (extrude the sketch)
module body() { linear_extrude(height = height) sketch_base(); }
// 5. ADDITIVE FEATURES (bosses, ribs, tabs)
module features_add() { ... }
// 6. SUBTRACTIVE FEATURES (holes, slots, pockets — ALWAYS LAST)
module features_cut() { ... }
// 7. ASSEMBLY (the Feature Tree)
difference() {
union() { body(); features_add(); }
features_cut();
}
```
**Profile-first design rules:**
- Prefer `polygon()` + `linear_extrude()` over `hull()` of 3D primitives
- Use `offset(r=radius)` for corner rounding instead of `hull()` with cylinders
- Use `rotate_extrude()` for axially symmetric parts (never stack cylinders)
- Define dimensions relative to edges/features, not absolute coordinates: `hole_x = total_length - edge_margin` (not magic numbers)
- Cascade tolerances from a single `fit_clearance` parameter
**Critical rules for generating OpenSCAD code:**
- Read `~/.claude/skills/openscad/references/language-reference.md` if unsure about syntax
- Always define parametric dimensions as variables at the top of the file
- Use `$fn = 64;` for smooth curves (or higher for final renders)
- Add comments explaining each section
- Use modules for reusable parts
- Keep wall thickness >= 1.2mm for FDM printing
- Design with the print orientation in mind (flat bottom, minimal overhangs)
### Step 4: Preview
Render a multi-angle PNG preview:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh preview ~/openscad-projects/<project-name>/src/main.scad
```
This generates 4 preview images (front, side, top, isometric) in the project's `previews/` directory.
### Step 5: Analyze Preview
Read each preview PNG using the Read tool to see the rendered object. Evaluate:
- Does the shape match the user's description?
- Are proportions correct?
- Are there visible artifacts or unintended geometry?
- Would this print well? (overhangs, bridging, thin walls)
Report findings to the user with the preview images.
### Step 6: Iterate
If changes are needed, edit the .scad file and re-render. Repeat Steps 4-5 until the user is satisfied. Each iteration should be targeted — change one aspect at a time.
### Step 7: Export
When the design is approved:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh export ~/openscad-projects/<project-name>/src/main.scad
```
This produces:
- `output/model.stl` — for slicing and printing
- `output/model.3mf` — alternative format (better metadata)
- `previews/final-preview.png` — high-res final render
---
## Workflow: Replicate Mode
When the user provides reference images of a physical object to reproduce in OpenSCAD:
### Step 1: Analyze Reference Images
Read ALL provided reference images using the Read tool. For each image, extract:
- **Overall shape**: What geometric primitives compose this object?
- **Proportions**: Relative dimensions (height-to-width ratio, etc.)
- **Features**: Holes, fillets, chamfers, textures, slots, lips, threads
- **Symmetry**: Is it symmetric along any axis?
- **Construction**: How would you decompose it into boolean operations?
If dimensions are provided, note them. If not, estimate proportions from the images and ask the user for at least one known measurement to establish scale.
### Step 2: Create Decomposition Plan
Before writing any code, describe the object as a series of OpenSCAD operations:
```
Object: Phone stand
Decomposition:
1. Base: flat rectangle with rounded corners (80x60x5mm)
2. Back support: angled plate (60x3mm, tilted 70 degrees)
3. Front lip: small ridge to hold phone (60x3x8mm)
4. Fillet: smooth transition between base and back support
5. Cable channel: cylinder subtracted from base center
```
Present this plan to the user for confirmation before coding.
### Step 3: Generate Initial .scad File
Write the OpenSCAD code based on the decomposition. Set up the project:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-project.sh init "<object-name>"
```
Write the .scad to the project's `src/main.scad`.
### Step 4: Render and Compare
Generate a preview from the **same angle** as the reference image:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh quick ~/openscad-projects/<name>/src/main.scad
```
Read both the reference image and the rendered preview. Compare them side by side mentally:
- Does the overall silhouette match?
- Are proportions correct?
- Are features (holes, edges, curves) in the right places?
- What's the biggest discrepancy?
### Step 5: Iterative Refinement Loop
For each discrepancy found:
1. Identify which part of the .scad code controls the mismatched feature
2. Make a **single targeted edit** to improve the match
3. Re-render from the same angle
4. Re-compare with the reference
**Refinement priorities** (fix in this order):
1. Overall shape and proportions
2. Major features (holes, cutouts, protrusions)
3. Angles and curves
4. Fillets, chamfers, and surface details
5. Fine details
### Step 6: Multi-Angle Validation
Once the primary angle looks good, render from all angles that have reference images:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh preview ~/openscad-projects/<name>/src/main.scad
```
Compare each rendered view against its corresponding reference image. Fix any angle-specific discrepancies.
### Step 7: Dimensional Verification
If the user provided measurements, add `echo()` statements to verify:
```openscad
echo("Total width:", width);
echo("Total height:", height);
echo("Wall thickness:", wall);
```
Render with echo capture to verify dimensions match specifications.
### Step 8: Export
When the user confirms the replication is satisfactory, export for printing.
### Tips for Accurate Replication
- **Start simple**: Begin with bounding-box primitives, then refine
- **Use reference dimensions**: If user says "it's about 10cm tall", anchor ALL proportions to that
- **Match camera angle**: Use `--camera` to match the reference photo's perspective
- **Organic shapes**: Approximate with hull(), minkowski(), or rotate_extrude() of a profile
- **Iterate small**: Change one thing per render cycle
- **Ask when unsure**: If a feature is ambiguous from the images, ask the user rather than guessing
---
## Workflow: Reconstruct Mode (STL-to-SCAD)
When the user provides an STL file and wants it converted to parametric OpenSCAD code:
### Overview
STL files are triangle meshes with no semantic information about the original primitives or operations that created them. Reconstruction is the process of analyzing the mesh geometry and re-expressing it as clean, parametric OpenSCAD code. This is valuable because:
- Parametric code can be modified (change dimensions, add features)
- OpenSCAD code is human-readable and version-controllable
- The resulting model can be adapted to different use cases
### Critical Rules for Reconstruction
**Read `references/reconstruction-guide.md` before starting any reconstruction.** It contains the complete best practices guide learned from real reconstructions.
**Rule 1: SCULPTOR APPROACH (mandatory).** Start from a full solid block, subtract ALL features. Never build up from pieces — CSG ordering bugs cause added material to cover previously-cut holes. Structure: `difference() { solid_body(); channels(); tapers(); ALL_holes_LAST(); }`
**Rule 2: NEVER add features based on assumptions.** Always verify with SVG contour data AND reference images. If a feature doesn't appear as a separate contour in the SVG slices, IT DOES NOT EXIST. Known hallucinations to avoid:
- Pyramids/cones from render shadows
- Cylinders from curved wall edges
- Top holes from through-hole exit points
**Rule 3: Bounding box match ≠ correct model.** 0.000mm bbox delta can mean only 70% geometric accuracy. Always use mesh comparison (`openscad-stl-compare.sh`) with boolean diff images.
**Rule 4: ANALYZE FIRST, DECOMPOSE, THEN CHOOSE per-component approach.**
Do NOT jump to code. The analysis phase must answer these questions:
1. **What are the dominant features?** (diagonal arm, clips, channels, holes)
2. **What is the thinnest axis?** That's the likely extrusion direction — NOT necessarily the stability score winner
3. **Can the object be decomposed into simpler sub-objects?** Model each with its best technique
4. **Where are internal channels/gaps?** Slice along Z to find multi-body cross-sections
**Rule 5: Choose the extrusion axis by geometry, not just stability score.**
The profile extractor's stability score finds the axis with the most uniform cross-section. But this is misleading for models with diagonal features — slicing along Z for a diagonal bracket produces staircase artifacts. Instead:
| Model Type | Best Extrusion Axis | Why |
|-----------|-------------------|-----|
| Flat bracket/plate | Thinnest axis (smallest extent) | Profile in the wide plane captures all detail |
| Diagonal/angled arm | Thinnest axis | Diagonals live in the plane of the two longest axes |
| Clean extrusion (stability < 0.1) | Stability-score axis | Profiles are identical → stability is reliable |
| Cylindrical (stability > 0.3) | Object's rotational axis | Use rotate_extrude or parametric primitives |
| Truly complex (no good axis) | Dense multi-axis slabbing | 2mm slabs along thinnest axis |
```bash
# Always run ALL analysis tools before writing any code:
bash ~/.claude/skills/openscad/scripts/openscad-stl-reconstruct.sh model.stl analysis/
python3 ~/.claude/skills/openscad/scripts/openscad-profile-extract.py model.stl --json analysis/profile.json
python3 ~/.claude/skills/openscad/scripts/openscad-adaptive-slice.py model.stl analysis/
```
After analysis, compare: **thinnest axis extent** vs **stability-score axis**. If they differ, the thinnest axis is usually better for models with angled features.
**Rule 6: Choose the right technique for each component:**
| Geometry | Best Approach | Expected Accuracy |
|----------|--------------|-------------------|
| Flat/angular (brackets, plates) | Profile extraction + linear_extrude | 90-96% |
| Diagonal features (angled arms, tapers) | Profile along thinnest axis + linear_extrude | 85-92% |
| Simple known shapes (stadium, box) | Parametric primitives + SDF optimizer | 90-96% |
| Cylindrical features (puzzle tabs, bosses) | Parametric circle() + square() | 85-95% |
| Smooth transitions (convex shapes only) | hull() between boundary profiles | 85-90% |
| Multi-width models (width varies along axis) | Dense X-slab (2mm profiles along thinnest axis) | ~92% |
| Mixed (curves + flats) | Polygon profile (hi-res, tol=0.02) | 70-80% |
| Complex organic shapes | import() original STL + parametric modifications | N/A |
**Rule 7: hull() ONLY for convex profiles.** Hull between two profiles creates the convex hull — it fills in ALL concavities (channels, clips, U-forks, hooks). Only use hull for simple solid zones with 1 contour and no holes. For concave profiles, use linear_extrude of a representative profile instead.
**Rule 8: Dense X-slab approach for complex models.**
When no single extrusion works, slice every 2mm along the thinnest axis:
1. At each X position, extract the full Y-Z cross-section (ALL bodies, not just the largest)
2. Extrude each slab for 2mm width
3. Union all slabs — gaps between bodies are naturally preserved
4. Note: this approach has a ~6% volume overestimate floor from polygon extraction artifacts. Below 6% requires hand-modeled parametric geometry.
**Use the automated reconstruction analysis FIRST — before writing any code:**
```bash
bash ~/.claude/skills/openscad/scripts/openscad-stl-reconstruct.sh model.stl output_dir/
```
This runs the full pipeline: mesh stats (trimesh), 2D profile slices (OpenSCAD projection), primitive detection (RANSAC/normal analysis), and generates SVG profiles at multiple Z levels. The SVG profile analysis is the MOST IMPORTANT output — it reveals the complete cross-section structure at each height level.
**The SVG Profile Method** (preferred over vertex analysis):
1. `projection(cut=true)` slices the STL at a Z height → exports 2D SVG
2. Parse the SVG to count contours: BODY (large area) vs HOLES (small area)
3. Compare contours at different Z levels to understand how the shape changes with height
4. This reveals: channels, slots, holes, wall thickness, taper angles — all from 2D data
**After analysis, verify with mesh comparison:**
```bash
bash ~/.claude/skills/openscad/scripts/openscad-stl-compare.sh original.stl reconstruction.stl output_dir/
```
Target: >95% geometric accuracy. Use diff images to identify remaining discrepancies.
### Step 1: Automated Analysis (run ALL tools)
```bash
# Tool 1: SVG profiling + primitive detection
bash ~/.claude/skills/openscad/scripts/openscad-stl-reconstruct.sh model.stl analysis/
# Tool 2: Profile extraction + extrusion axis detection
python3 ~/.claude/skills/openscad/scripts/openscad-profile-extract.py model.stl \
--output analysis/profile.scad --json analysis/profile.json
# Tool 3: Adaptive multi-axis feature map
python3 ~/.claude/skills/openscad/scripts/openscad-adaptive-slice.py model.stl analysis/
```
**Key outputs to examine:**
- `analysis/slices/*.svg` — 2D profiles at 5 Z levels
- `analysis/profile.json` — extrusion axis, stability score, profile points, hole count
- `analysis/adaptive-slicing.json` — feature zones on all 3 axes, transition locations
- `analysis/primitives.json` — detected cylinders/planes
- `analysis/mesh-info.json` — volume, dimensions, symmetry
### Step 1b: Understand the Object (BEFORE writing code)
After running analysis tools, render multi-angle previews and answer:
1. **What are the main components?** (e.g. "bottom clip + diagonal arm + top clip")
2. **Which axis is thinnest?** Compare extents — the thinnest is likely the extrusion direction
3. **Are there diagonal/angled features?** If yes, the stability-score axis is probably WRONG
4. **Where do cross-sections change?** Check the adaptive slicer's transition zones
5. **Are there multi-body zones?** (channels, rails, gaps between parts)
**Decision tree — axis selection:**
1. If `stability_score < 0.1` AND thinnest axis matches stability axis → clean extrusion, use `profile.scad`
2. If model has **diagonal features** → use the **thinnest axis** regardless of stability score
3. If `stability_score < 0.3` AND no diagonals → stability axis + feature variations
4. If `stability_score > 0.3` → complex shape. Try dense X-slab along thinnest axis, or decompose into sub-objects
**Decision tree — technique per component:**
- Simple extruded body → profile + linear_extrude along extrusion axis
- Diagonal arm/strut → profile along thinnest axis captures it naturally
- Clips, hooks, U-channels → profile extraction (NOT hull — hull fills concavities)
- Cylindrical features → parametric circle() + square(), NOT polygon profiles
- Smooth convex transitions → hull() between boundary profiles (ONLY if convex)
- Complex multi-width → dense 2mm slabs along thinnest axis
**For models with cylindrical features** (stability > 0.3 or SVG shows circular contours):
- Do NOT rely on polygon profiles — they approximate curves poorly
- Identify circle centers and radii from the SVG contour data
- Model with `circle()` + `square()` in 2D, then extrude
Then render multi-angle previews:
```openscad
// Temporary viewer file
import("path/to/model.stl");
```
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh preview /tmp/stl-viewer.scad
```
Read all preview images to understand the 3D shape from multiple angles.
### Step 1b: Auto-Reconstruction (NEW — recommended for most models)
After running the adaptive slicer, use the auto-reconstructor to generate parametric .scad directly:
```bash
# Option A: With pre-computed analysis
python3 ~/.claude/skills/openscad/scripts/openscad-auto-reconstruct.py model.stl \
--analysis analysis/ --output project/src/main.scad
# Option B: Run analysis + reconstruction in one step
python3 ~/.claude/skills/openscad/scripts/openscad-auto-reconstruct.py model.stl \
--output project/src/main.scad --run-analysis
# Option C: Tighter circle fitting for precision parts
python3 ~/.claude/skills/openscad/scripts/openscad-auto-reconstruct.py model.stl \
--analysis analysis/ --output project/src/main.scad --circle-threshold 0.3
```
This automatically:
1. Parses the feature map JSON into zones
2. Extracts profiles at zone boundaries
3. Fits circles/arcs to replace polygon approximations (Feature 3)
4. Generates hull() blends for transition zones (Feature 2)
5. Emits one OpenSCAD module per zone with sculptor assembly (Feature 1)
The output is a good starting point — review and refine the generated .scad, then verify with mesh comparison. For models with cylindrical features, this typically achieves >85% accuracy automatically (vs 75% with polygon-only).
### Step 2: Detailed Structure Mapping
**For simple models** (5 SVG slices are enough):
Parse the SVG contours to count bodies vs holes at each Z level.
**For complex models** (brackets, enclosures with multiple features):
Use the adaptive multi-axis slicer for efficient feature detection:
```bash
python3 ~/.claude/skills/openscad/scripts/openscad-adaptive-slice.py model.stl analysis/
```
This automatically: scans all 3 axes with coarse pass (5mm) → detects transitions → fine pass (0.5mm) around transitions. Produces a feature map classifying each zone as `solid`, `shell_or_channel`, `multi_body`, or `complex`.
For manual fine-grained slicing at specific heights:
```bash
for z in $(seq 0.5 1 <max_z>); do
echo "projection(cut=true) translate([0,0,-$z]) import(\"model.stl\");" > /tmp/s.scad
openscad -o "slices/z${z}.svg" /tmp/s.scad
done
```
Parse each SVG to build a structural map:
```
Z=0-5: 1 body (full width) + 8 holes → Solid base with screw holes
Z=5-10: 2 bodies + 8 holes → Channel appeared, walls split
Z=10-20: 2 bodies narrowing → Taper zone (measure rate)
Z=20-33: 2 bodies constant width → Top section
Z=25-27: Bodies interrupted → Counterbore pockets at this depth
```
**Hole positions from SVG centroids** — for each hole contour at a given Z, compute the centroid. This gives exact X,Y positions far more reliably than vertex analysis.
**Feature verification rule:** If a feature doesn't appear as a distinct contour in the SVG data, IT DOES NOT EXIST in the model. Never add features based on visual interpretation of 3D renders alone.
### Step 3: Choose Approach and Decompose
Based on the analysis data, choose the reconstruction approach:
**Approach A — Profile Extrusion** (for extruded parts, stability < 0.3):
```bash
# The profile extractor already generated the .scad — use it as a starting point
cat analysis/profile.scad
# Adjust: add cavity with offset(delta=-wall), add floor, add features
```
**Approach B — Parametric Primitives** (for known shapes or cylindrical features):
Create a decomposition plan using measured dimensions from SVG data:
```
Decomposition:
1. Base: square([80, 80]) + circle tabs — from SVG outer contour at Z=mid
2. Cavity: offset(delta=-wall) of base — from SVG inner contour
3. Floor: solid at Z=0 to floor_h — from SVG at Z=0 (1 contour = solid)
4. Holes: cylinder(d=3) at SVG hole centroids
5. Counterbores: cylinder(d=8, h=2) at same positions
```
**Approach C — Hybrid** (for complex shapes with both flat and curved features):
1. Extract polygon profile for the overall outline
2. Identify which curves in the profile are circles (regular spacing, arc-like)
3. Replace those polygon sections with parametric `circle(r)` operations
4. Assemble: `square() + circle()` union for tabs, `difference()` for slots
**Counterbore vs Countersink** — always verify from reference images:
- **Counterbore**: flat cylindrical pocket (`cylinder(d=cb_d, h=cb_depth)`)
- **Countersink**: conical taper (`cylinder(d1=cs_d, d2=hole_d, h=cs_depth)`)
- Most 3D-printed parts use counterbores, not countersinks
### Step 4: Write Parametric .scad Code
Create a new project and write the reconstructed code:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-project.sh init "<name>-reconstructed"
```
**Key principles for reconstruction:**
- Extract ALL dimensions as named variables at the top
- Use meaningful variable names that describe the physical feature
- Add comments linking each section to the original STL features
- Include `echo()` statements for bounding box verification
- Add `assert()` for parameter ranges
### Step 5: Visual Comparison Loop
Render the reconstructed .scad and compare side-by-side with the original STL renders:
1. Render the reconstruction from the same camera angles as Step 1
2. Read both sets of images
3. Compare silhouettes, proportions, and feature placement
4. Identify the biggest discrepancy
5. Fix it and re-render
6. Repeat until the reconstruction matches the original
### Step 6: Overlay Verification
For precise verification, create an overlay .scad file:
```openscad
// Overlay: original STL (transparent) vs reconstruction
%import("path/to/original.stl"); // % = transparent background
color("red", 0.6) reconstructed_model();
```
Render this overlay — any RED areas visible through the transparent original indicate reconstruction errors. Any grey areas not covered by red indicate missing geometry.
### Step 7: Mesh-to-Mesh Comparison
**This is the most important verification step.** Export the reconstruction as STL and compare it against the original using boolean difference:
```bash
# Export reconstruction
bash ~/.claude/skills/openscad/scripts/openscad-render.sh stl ~/openscad-projects/<name>/src/main.scad
# Run mesh comparison
bash ~/.claude/skills/openscad/scripts/openscad-stl-compare.sh \
path/to/original.stl \
~/openscad-projects/<name>/output/main.stl \
~/openscad-projects/<name>/previews/comparison
```
This produces:
- **diff-A-minus-B.png** — geometry in original but MISSING from reconstruction (what you need to add)
- **diff-B-minus-A.png** — EXTRA geometry in reconstruction not in original (what you need to remove)
- **overlay.png** — both models overlaid for visual check
- **Geometric accuracy %** — based on volume of boolean differences vs original volume
**Target: >95% geometric accuracy.** If below 95%, examine the diff images to identify which features are wrong, fix them, re-export, and re-compare. Iterate until accuracy is satisfactory.
**Important:** Bounding box delta can be 0.000mm while geometric accuracy is only 78% — internal features matter more than outer dimensions.
### Step 8: Dimensional Verification
Compare echo output from the reconstruction with the STL bounding box:
```openscad
echo(str("Reconstructed BBOX: ", width, " x ", depth, " x ", height));
```
### Step 9: SDF Parameter Optimization (Advanced)
If the SVG profile method doesn't achieve >95% accuracy, use the SDF optimizer for automatic parameter tuning:
```bash
python3 ~/.claude/skills/openscad/scripts/openscad-sdf-optimize.py \
path/to/original.stl \
stadium-slot \
--verbose \
--output analysis/sdf-result.json
```
This works by:
1. Sampling 30,000 random points in the bounding box
2. Computing target occupancy (inside/outside original mesh) via trimesh
3. Defining the reconstruction as a parametric SDF (Signed Distance Field)
4. Using `scipy.optimize.minimize(method="Powell")` to maximize IoU (Intersection over Union)
5. Generating OpenSCAD code with optimized parameters
**When to use**: When you know the correct model topology (e.g., "stadium body with cylindrical slot") but can't find the exact parameters. The optimizer finds them automatically.
**Supported model types**: `stadium-slot`, `box-holes`. Add new types by defining an SDF function in the script.
**Workflow**: Run `openscad-stl-reconstruct.sh` first (to identify the model topology), then `openscad-sdf-optimize.py` (to find exact parameters), then `openscad-stl-compare.sh` (to verify).
**Prerequisites**: `pip3 install trimesh numpy scipy rtree`
### Common Pitfalls
- **Bounding box match ≠ correct model.** A model with completely wrong internal geometry can still have a 0.000mm bounding box delta. Always verify visually from multiple angles.
- **Don't assume features from renders alone.** What looks like a cylinder in a top-down view might just be a curved wall edge. Always verify with vertex analysis.
- **Coincident faces cause Z-fighting.** If a feature touches the body boundary exactly, use `intersection()` to clip it cleanly rather than making it the exact same size.
- **Don't flip between adding and removing features.** If unsure whether a feature exists, run cross-section analysis before deciding. Oscillating between "add bar" and "remove bar" wastes iterations.
- **Offset features are common.** Cylinders, holes, and channels are often NOT centered. Always calculate the actual center from vertex data rather than assuming symmetry.
### Limitations
- **Organic shapes** (sculpted, freeform surfaces) cannot be fully reconstructed as primitives. For these, keep the STL import and wrap it in a module.
- **Very complex models** (1000+ features) should be reconstructed incrementally, starting with the major body and adding features one group at a time.
- **Thread geometry** in STL is extremely difficult to reconstruct. Use `threads.scad` library instead of trying to match individual thread faces.
- **Text/engravings** embedded in STL meshes are very hard to extract. It's better to re-add text using OpenSCAD's `text()` module.
### Hybrid Approach
For complex models, use a hybrid strategy:
```openscad
// Import the complex organic base from STL
module original_base() {
import("base-section.stl");
}
// Reconstruct and parameterize the mechanical features
module mounting_bracket(width=30, hole_d=5) {
difference() {
original_base();
// Add parametric mounting holes
for (pos = hole_positions)
translate(pos) cylinder(d=hole_d, h=50, center=true);
}
}
```
This lets the user modify the parametric parts while keeping the complex geometry intact.
---
## Workflow: Refine Mode
When the user wants to modify an existing design:
### Step 1: Read the Existing File
```bash
# Find .scad files in the project
```
Read the .scad source to understand the current design.
### Step 2: Render Current State
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh preview /path/to/file.scad
```
Read the preview images to see what currently exists.
### Step 3: Apply Changes
Edit the .scad file with the requested modifications. Use the Edit tool for surgical changes.
### Step 4: Re-render and Compare
Generate new previews and visually compare with the previous version. Report what changed.
### Step 5: Repeat or Export
Continue iterating or export when satisfied.
---
## Workflow: Export Mode
Quick export of an existing .scad file:
```bash
# Single format
bash ~/.claude/skills/openscad/scripts/openscad-render.sh stl /path/to/file.scad
# Multiple formats
bash ~/.claude/skills/openscad/scripts/openscad-render.sh export /path/to/file.scad
# With parameter overrides
bash ~/.claude/skills/openscad/scripts/openscad-render.sh stl /path/to/file.scad -D 'width=50' -D 'height=30'
```
---
## Workflow: Analyze Mode
Review a design for printability:
```bash
bash ~/.claude/skills/openscad/scripts/openscad-render.sh analyze /path/to/file.scad
```
This renders cross-section views and reports:
- Object bounding box dimensions
- Whether the mesh is manifold (watertight)
- Estimated print time indicators (volume, surface area from STL)
- Visual check of overhangs via bottom-up view
---
## Script Reference
All scripts live in `~/.claude/skills/openscad/scripts/`:
| Script | Purpose |
|--------|---------|
| `openscad-render.sh` | Core render/export/preview engine |
| `openscad-project.sh` | Project scaffolding and management |
| `openscad-validate.sh` | Strict validation with categorized error output |
| `openscad-stl-analyze.sh` | STL mesh analysis: bbox, cross-sections, gap detection |
| `openscad-stl-compare.sh` | Mesh comparison: boolean diff, volume delta, accuracy % |
| `openscad-stl-reconstruct.sh` | Automated STL analysis: profiles, primitives, CSG inference |
| `openscad-sdf-optimize.py` | SDF-based parameter optimizer (IoU scoring, no OpenSCAD in loop) |
| `openscad-adaptive-slice.py` | Adaptive multi-axis slicing (coarse→transitions→fine on X,Y,Z) |
| `openscad-auto-reconstruct.py` | Auto-translate feature map → parametric .scad (circle fitting, hull blending) |
### openscad-render.sh Commands
```bash
# Quick single preview (isometric)
openscad-render.sh quick <file.scad>
# Multi-angle preview (4 views)
openscad-render.sh preview <file.scad>
# Export STL only
openscad-render.sh stl <file.scad> [-D 'var=val' ...]
# Export all formats (STL + 3MF + PNG)
openscad-render.sh export <file.scad> [-D 'var=val' ...]
# Analyze printability
openscad-render.sh analyze <file.scad>
# Custom render
openscad-render.sh custom <file.scad> --format png --imgsize 1920,1080 --camera 0,0,0,45,0,30,200
```
### openscad-project.sh Commands
```bash
# Initialize new project
openscad-project.sh init <project-name>
# List projects
openscad-project.sh list
# Clean build artifacts
openscad-project.sh clean <project-name>
```
---
## OpenSCAD Code Guidelines
### File Structure Convention
```openscad
// ============================================
// Project: <name>
// Description: <what this models>
// Author: Claude Code + User
// ============================================
// --- Parameters (user-configurable) ---
width = 50; // [mm] overall width
height = 30; // [mm] overall height
depth = 20; // [mm] overall depth
wall = 2.0; // [mm] wall thickness
tolerance = 0.3; // [mm] printer tolerance
// --- Rendering quality ---
$fn = 64; // curve smoothness (use 128+ for final export)
eps = 0.01; // epsilon for clean boolean operations
// --- Derived dimensions ---
inner_width = width - 2 * wall;
inner_height = height - 2 * wall;
// --- Main model ---
main_assembly();
// --- Modules ---
module main_assembly() {
// ...
}
```
### 3D Printing Best Practices in OpenSCAD
- **Wall thickness**: minimum 1.2mm for FDM (2-3 perimeters with 0.4mm nozzle)
- **Tolerance**: 0.2-0.3mm clearance for fitting parts together (peg-in-hole, snap fits)
- **Overhangs**: keep below 45 degrees from vertical, or add supports in design
- **Chamfer vs fillet**: prefer chamfers on downward-facing surfaces (avoids supports); use fillets on top surfaces
- **Bridging**: max ~10mm unsupported spans
- **First layer**: design flat bottoms for bed adhesion; largest flat surface on build plate
- **Epsilon constant**: always define `eps = 0.01;` and use it in boolean operations to prevent Z-fighting / coplanar faces
- **Manifold geometry**: always ensure boolean operations produce valid solids; operands must overlap
- **Resolution**: use `$fn = 64` for preview, `$fn = 128` for export
- **Design intent**: Define hole positions relative to edges (`hole_x = length - margin`), never as absolute coordinates
- **Tolerance chains**: Define a single `fit_clearance` parameter and derive all clearances from it
- **Assert validation**: Use `assert()` to validate parameters: `assert(wall >= 1.2)`, `assert(boss_d > hole_d + 2*wall)`
- **Profile-first**: Use `offset(r=corner_r)` on 2D `polygon()` instead of `hull()` with 3D cylinders
### Common Patterns
**Rounded box:**
```openscad
module rounded_box(size, radius) {
minkowski() {
cube([size.x - 2*radius, size.y - 2*radius, size.z - radius]);
cylinder(r=radius, h=radius);
}
}
```
**Shell (hollow object):**
```openscad
module shell(outer_size, wall) {
difference() {
cube(outer_size);
translate([wall, wall, wall])
cube([outer_size.x - 2*wall, outer_size.y - 2*wall, outer_size.z]);
}
}
```
**Screw hole with countersink:**
```openscad
module screw_hole(d=3, h=10, cs_d=6, cs_h=2) {
union() {
cylinder(d=d, h=h);
translate([0, 0, h - cs_h])
cylinder(d1=d, d2=cs_d, h=cs_h);
}
}
```
---
## Available Libraries
Popular libraries that can be installed for advanced features:
| Library | Use Case | Install |
|---------|----------|---------|
| **BOSL2** | Swiss-army knife: attachments, shapes, threading, paths | `git clone https://github.com/BelfrySCAD/BOSL2 ~/.local/share/OpenSCAD/libraries/BOSL2` |
| **NopSCADlib** | Vitamins (screws, nuts, electronics, bearings) | `git clone https://github.com/nophead/NopSCADlib ~/.local/share/OpenSCAD/libraries/NopSCADlib` |
| **threads.scad** | Metric threads, hex bolts, nuts | `git clone https://github.com/rcolyer/threads-scad ~/.local/share/OpenSCAD/libraries/threads` |
| **Round-Anything** | Smooth fillets and rounding | `git clone https://github.com/Irev-Dev/Round-Anything ~/.local/share/OpenSCAD/libraries/Round-Anything` |
| **YAPP_Box** | Parametric project enclosures | `git clone https://github.com/mrWheel/YAPP_Box ~/.local/share/OpenSCAD/libraries/YAPP_Box` |
| **Catch'n'Hole** | Nut catches, screw holes | `git clone https://github.com/mmalecki/catchnhole ~/.local/share/OpenSCAD/libraries/catchnhole` |
Check installed libraries:
```bash
ls ~/.local/share/OpenSCAD/libraries/ 2>/dev/null
ls /opt/homebrew/share/openscad/libraries/ 2>/dev/null
```
When user needs a library, install it and add `use <library/file.scad>` to the .scad source.
---
## Error Handling
When OpenSCAD fails:
1. **Parse errors**`ERROR: Parser error: syntax error in file X, line Y`
- Read the .scad file at the reported line
- Fix syntax (common: missing semicolons, unmatched braces/parens, wrong function names)
- Re-render
2. **Geometry errors**`WARNING: Object may not be a valid 2-manifold`
- Check boolean operations aren't creating degenerate geometry
- Ensure shapes overlap properly for difference/intersection
- Add small epsilon offsets (0.01mm) to prevent coplanar faces
3. **Rendering timeouts** — complex models with high `$fn`
- Lower `$fn` for preview (32), raise for export (128)
- Simplify geometry where possible
- Use `render()` to cache intermediate results
4. **Empty output** — model produces no geometry
- Check that modules are actually called
- Verify boolean operations don't subtract everything
- Use `echo()` statements to debug variable values
Always capture stderr when rendering — it contains warnings and errors:
```bash
openscad -o output.stl input.scad 2>&1
```
---
## Camera Presets for Multi-View
| View | Camera Parameters |
|------|-------------------|
| Front | `--camera 0,0,0,90,0,0,<dist>` |
| Back | `--camera 0,0,0,90,0,180,<dist>` |
| Right | `--camera 0,0,0,90,0,90,<dist>` |
| Left | `--camera 0,0,0,90,0,270,<dist>` |
| Top | `--camera 0,0,0,0,0,0,<dist>` |
| Bottom | `--camera 0,0,0,180,0,0,<dist>` |
| Isometric | `--autocenter --viewall` (default) |
| 3/4 view | `--camera 0,0,0,55,0,25,<dist>` |
Use `--autocenter --viewall` to auto-calculate distance, or specify explicit distance for consistent framing across iterations.