# 3d Print

> Search-first 3D printing workflow — finds real specs before modeling, generates manifold-safe models for Bambu Studio / PrusaSlicer / Cura / Fusion 360

- Skill: `edwinjj1/3d-print` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add edwinjj1/3d-print`
- Raw SKILL.md: https://api.skillmd.com/api/skills/edwinjj1/3d-print/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: edwinjj1 (https://skillmd.com/u/edwinjj1)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/edwinjj1/3d-print

---


# 3D Print Skill

## Purpose

Generate physically accurate, print-ready 3D models without fabricating dimensions.
Always search for real specs first. Never guess measurements.

---

## Workflow

### Step 1 — Search for real specs

First, try the local search script:
```bash
python3 scripts/search_specs.py "Canon EF 40mm f2.8 lens outer diameter"
```

**If the script returns no concrete millimeter values** (status: `no_instant_answer` or vague text),
do NOT stop. Use your own `search_web` tool to query the product's official datasheet,
spec page, or Wikipedia. Extract the three key numbers: outer dimension, inner dimension, height.
Only proceed to Step 2 once you have real numbers with a source you can cite.

All results are cached to `specs_cache.json`. On a cache hit, read the cached result and
extract dimensions directly — no re-search needed.

### Step 2 — Decide: preset or custom script

**Check if the request matches a preset:**
```bash
python3 scripts/generate_model.py --preset cube     # 20mm calibration cube
python3 scripts/generate_model.py --preset tube     # hollow cylinder
python3 scripts/generate_model.py --preset bracket  # box with through-hole
```

**If the request does NOT match a preset** (e.g. "lens cap with snap-fit tabs",
"hook with chamfer", "enclosure with lip", "multi-part assembly"), write a custom
Python script on the fly. Use the pattern:

```python
import sys
sys.path.insert(0, "scripts")
import manifold3d as m3d
from generate_model import export_3mf, export_stl
from pathlib import Path

# --- build geometry using manifold3d primitives ---
# See references/manifold-examples.md for API reference:
# extrusion, revolve, hull, chamfer, loft, snap-fit tabs, threads

body = m3d.Manifold.cylinder(height=8.0, radius_low=34.0, circular_segments=64)
inner = m3d.Manifold.cylinder(height=8.2, radius_low=33.0, circular_segments=64)
result = body - inner.translate([0, 0, -0.1])

# --- export ---
out = Path("output")
export_3mf(result, out / "lens_cap.3mf", name="lens_cap")
export_stl(result, out / "lens_cap.stl")
```

Save the script as `output/<name>_build.py`, run it, then proceed to Step 3.
The `export_3mf` and `export_stl` functions from `generate_model.py` handle all
file-format details — just call them with the final `Manifold` object.

### Step 3 — Validate the mesh

```bash
python3 scripts/validate_mesh.py output/lens_cap.stl
```

**If validation fails**, do NOT just run `--fix` and move on.
Read the specific failing checks, trace them back to the geometry construction,
and fix the root cause in the build script. Common fixes:

- `watertight: FAIL` → a boolean operation left an open edge; add a small Z-offset
  (e.g. `translate([0, 0, -0.1])`) on the subtracted body so it protrudes past the face
- `positive_volume: FAIL` → normals are inverted; the outer shell may be smaller than
  the inner — check radii
- `no_degenerate_faces: FAIL` → a zero-thickness wall from a tight boolean;
  add at least 0.4mm clearance between faces

After fixing, re-run the build script and re-validate. Repeat until all critical checks pass.

### Step 4 — Slice

Import `.3mf` into Bambu Studio / PrusaSlicer / Cura.
Expected: 0 non-manifold errors, 0 open edges.

---

## Spec Extraction from Cache

When `specs_cache.json` already contains an entry for the target part, load and extract
dimensions directly instead of re-running search:

```python
import json
cache = json.load(open("specs_cache.json"))
# find the entry whose query matches your part
# read result["answer"] or result["abstract"]
# parse out numeric values (mm) using your own reasoning
# map to: outer_diameter, inner_diameter, height, wall_thickness, etc.
```

Use your reasoning to map prose descriptions ("67.5mm filter thread") to the correct
variable in the build script. Do not ask the user to do this mapping manually.

---

## Reference Files

| File | Contents |
|------|----------|
| `references/formats.md` | STL vs 3MF vs STEP — when to use each |
| `references/tolerances.md` | FDM accuracy, shrinkage, fit clearances |
| `references/design-rules.md` | Manifold rules, wall thickness, overhang limits |
| `references/manifold-examples.md` | manifold3d API: extrusion, revolve, hull, chamfer, snap-fit, threads |

---

## Key Constraints

- **No fabricated dimensions** — search first, use real numbers with a cited source
- **manifold3d for all geometry** — guarantees no non-manifold edges
- **Custom script when no preset fits** — `generate_model.py` is a reference, not a limit
- **3MF primary output** — ISO/IEC 25422:2025, manifold-safe, Bambu/Prusa/Cura compatible
- **Fix root cause on validation failure** — do not rely on `--fix` alone

---

## Common Errors

| Error | Cause | Fix |
|-------|-------|-----|
| "244 non-manifold edges" in Bambu | numpy-stl or bad boolean | Regenerate with `manifold3d` |
| Hole too tight | FDM shrinks holes | Add +0.1–0.2mm radius compensation (tolerances.md) |
| Part snaps during press fit | No interference margin | 0.1–0.3mm interference (tolerances.md) |
| Overhangs fail | >45° without supports | Redesign or add supports in slicer |
| `watertight: FAIL` | Subtracted body flush with face | Add Z-offset of 0.1mm on subtracted body |
| `search_specs.py` returns empty | DuckDuckGo Instant Answer has no entry | Use `search_web` tool on manufacturer datasheet |

---

## Install

```bash
ln -sf /path/to/3d-print-skill ~/.claude/skills/3d-print
```

## Dependencies

```bash
pip install manifold3d trimesh numpy requests
```

