431 lines
17 KiB
Markdown
431 lines
17 KiB
Markdown
# STEP File Processor Skill
|
||
|
||
## Purpose
|
||
This skill enables Claude to work with STEP/STP CAD files used in manufacturing.
|
||
It provides:
|
||
- PNG thumbnail extraction at 6 camera angles
|
||
- Parts manifest / BOM export to CSV with automatic Chinese→English translation
|
||
- Optional English-labeled STEP copy (source file never modified)
|
||
- Natural language geometric query interface (holes, faces, dimensions, quantities)
|
||
|
||
Designed for MPMedia's display enclosure and mounting kit product lines.
|
||
|
||
---
|
||
|
||
## When to invoke this skill
|
||
|
||
Trigger on any of:
|
||
- User provides a `.step` or `.stp` file path and asks to process, view, extract, or query it
|
||
- User asks to "generate thumbnails" or "render views" of a STEP file
|
||
- User asks to "extract BOM", "get parts list", or "show manifest" from a STEP file
|
||
- User asks a geometric question about a STEP file: holes, faces, dimensions, quantities
|
||
- User asks to "translate" a STEP file's part names
|
||
- Claude Code context where a STEP file is present in the working directory
|
||
|
||
**Read this entire SKILL.md before writing any code.**
|
||
|
||
---
|
||
|
||
## Library Stack
|
||
|
||
### Primary: build123d
|
||
- Modern OpenCASCADE Python wrapper; native Apple Silicon arm64 wheels
|
||
- Clean, Pythonic API for STEP reading, assembly traversal, geometry queries
|
||
- Offscreen PNG rendering via: build123d → export GLB/OBJ → trimesh → pyrender
|
||
- Install: `pip install build123d trimesh pyrender pillow numpy pandas anthropic`
|
||
- ARM64 OCP wheel required first — see INSTALL.md
|
||
|
||
### Fallback: FreeCAD headless
|
||
- Native macOS-arm64 build available via conda-forge (verified Nov 2024)
|
||
- Used when build123d fails to load or render a specific file
|
||
- Invoked via subprocess calling `FreeCADCmd` or by importing `FreeCAD` + `Part`
|
||
- **Never import FreeCADGui** — headless only, GUI modules will crash
|
||
- Install: `conda install -c conda-forge freecad` (arm64 native, no Rosetta)
|
||
|
||
### Translation: Claude API (claude-sonnet-4-20250514)
|
||
- Translates Chinese part names with manufacturing context awareness
|
||
- e.g. 安装支架 → "Mounting Bracket" (not just "installation support")
|
||
- Requires ANTHROPIC_API_KEY in environment
|
||
|
||
### Supporting libraries (both tracks)
|
||
- `Pillow` — PNG save/compose
|
||
- `numpy` — geometry math
|
||
- `pandas` — CSV/BOM output
|
||
- `trimesh` + `pyrender` — GLB→PNG rendering fallback path
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
```
|
||
step-processor/
|
||
step_processor.py ← CLI entry point + REPL
|
||
modules/
|
||
loader.py ← STEP loading (build123d primary, FreeCAD fallback)
|
||
bom.py ← Assembly tree traversal + BOM extraction
|
||
renderer.py ← Offscreen PNG rendering (6 views)
|
||
translator.py ← Claude API translation layer
|
||
rewriter.py ← STEP label patcher (_EN copy only, never modifies source)
|
||
query_engine.py ← Geometric query handler (holes, faces, dimensions)
|
||
INSTALL.md
|
||
SKILL.md
|
||
```
|
||
|
||
---
|
||
|
||
## Dual-Track Fallback Pattern
|
||
|
||
Every module must implement this pattern:
|
||
|
||
```python
|
||
def load_step(filepath):
|
||
try:
|
||
return _load_via_build123d(filepath)
|
||
except ImportError:
|
||
logger.warning("build123d not available — falling back to FreeCAD")
|
||
return _load_via_freecad(filepath)
|
||
except Exception as e:
|
||
logger.warning(f"build123d failed ({e}) — falling back to FreeCAD")
|
||
return _load_via_freecad(filepath)
|
||
```
|
||
|
||
Always log fallbacks at WARNING level so the user knows which path ran.
|
||
|
||
FreeCAD fallback invocation pattern (subprocess, cleanest isolation):
|
||
```python
|
||
import subprocess, json, tempfile
|
||
|
||
def _load_via_freecad(filepath):
|
||
script = f"""
|
||
import FreeCAD, Part, json
|
||
shape = Part.read("{filepath}")
|
||
# extract data, write to temp JSON
|
||
"""
|
||
result = subprocess.run(["FreeCADCmd", "--console", "-c", script], capture_output=True)
|
||
# parse result...
|
||
```
|
||
|
||
---
|
||
|
||
## Camera Views
|
||
|
||
Always produce these 6 views unless user specifies otherwise:
|
||
|
||
| View Name | Camera Direction | File Suffix |
|
||
|------------|---------------------------|------------------|
|
||
| Front | +Y looking toward origin | `_front.png` |
|
||
| Bottom | -Z looking up | `_bottom.png` |
|
||
| Left | -X looking right | `_left.png` |
|
||
| Right | +X looking left | `_right.png` |
|
||
| Iso Left | +X+Y+Z (left-forward) | `_iso_left.png` |
|
||
| Iso Right | -X+Y+Z (right-forward) | `_iso_right.png` |
|
||
|
||
Default resolution: 1024×768. All PNGs saved to same folder as source STEP file.
|
||
|
||
---
|
||
|
||
## Output Naming Convention
|
||
|
||
Given input: `/path/to/EnclosureA.step`
|
||
|
||
| Output | Path |
|
||
|---------------------|-----------------------------------|
|
||
| Thumbnails (×6) | `/path/to/EnclosureA_front.png` etc. |
|
||
| BOM CSV | `/path/to/EnclosureA_bom.csv` |
|
||
| English STEP copy | `/path/to/EnclosureA_EN.step` |
|
||
|
||
**Never overwrite or modify the source STEP file.**
|
||
|
||
`_EN.step` is only created when Chinese labels are detected. If all labels are
|
||
already ASCII/English, skip silently and note it in output.
|
||
|
||
---
|
||
|
||
## BOM CSV Schema
|
||
|
||
```
|
||
part_number, part_name_original, part_name_english, quantity, level, parent,
|
||
bbox_x_mm, bbox_y_mm, bbox_z_mm, notes
|
||
```
|
||
|
||
- `level` = assembly depth (0 = root, 1 = direct children, etc.)
|
||
- `parent` = part_name_english of parent assembly
|
||
- `notes` = translator flags (e.g. "machine-translated", "ambiguous term")
|
||
- Bounding boxes in millimeters, 2 decimal places
|
||
- If no translation needed, `part_name_original` == `part_name_english`
|
||
|
||
---
|
||
|
||
## Translation Logic
|
||
|
||
```
|
||
bom.py extracts all part names
|
||
→ translator.py checks each name for CJK unicode range (\u4e00-\u9fff)
|
||
→ if any found: batch call Claude API with this system prompt:
|
||
|
||
"You are a mechanical engineering translator specializing in Chinese
|
||
manufacturing CAD files for display and enclosure products. Translate
|
||
the following part names from Chinese to English. Preserve technical
|
||
precision. Use standard hardware terminology. Output ONLY a JSON object
|
||
mapping original → translated, nothing else.
|
||
Example: {\"安装支架\": \"Mounting Bracket\", \"螺钉M4\": \"M4 Screw\"}"
|
||
|
||
→ inject translations into BOM CSV
|
||
→ if translation requested: produce _EN.step via rewriter.py
|
||
```
|
||
|
||
Flag uncertain translations in the `notes` column rather than silently guessing.
|
||
|
||
---
|
||
|
||
## STEP Label Rewriting (_EN copy)
|
||
|
||
`rewriter.py` produces a translated copy — line-by-line, targeted replacement:
|
||
|
||
1. Read source file line by line
|
||
2. Identify lines containing `PRODUCT('`, `PRODUCT_DEFINITION_CONTEXT(` etc.
|
||
3. Extract quoted name strings (first argument of PRODUCT entity)
|
||
4. Replace CJK strings with translated equivalents from the translation map
|
||
5. Write to `{filename}_EN.step`
|
||
6. Validate: entity `#` count before == after. If mismatch → abort, warn, delete partial output
|
||
|
||
**Do not use broad regex that could touch entity reference numbers (#123 etc.)**
|
||
Target only the quoted string arguments of known STEP entity types.
|
||
|
||
---
|
||
|
||
## Geometric Query Engine
|
||
|
||
`query_engine.py` handles natural language queries about loaded geometry.
|
||
|
||
### Supported Query Types
|
||
|
||
| Query pattern | What it extracts |
|
||
|----------------------------------|-------------------------------------------------------|
|
||
| "list all mounting holes" | Cylindrical faces, axis ⊥ to primary face, dia < 15mm|
|
||
| "list all holes" | All cylindrical through-features |
|
||
| "holes diameter [N]mm" | Filter holes by diameter |
|
||
| "tapped holes" / "threaded" | Cylinders with helical features (if present in model) |
|
||
| "face count" | Total face count by type (planar, cylindrical, etc.) |
|
||
| "bounding box" | Overall model extents in mm |
|
||
| "part [name] dimensions" | Bounding box of a specific named part |
|
||
| "largest face" | Largest planar face area in mm² |
|
||
| "wall thickness" | Min distance between opposing parallel faces |
|
||
| "all parts" | Full assembly listing with quantities |
|
||
|
||
### Query Invocation
|
||
|
||
**CLI (single query, exit after):**
|
||
```bash
|
||
python step_processor.py enclosure.step --query "list all mounting holes with diameter and depth"
|
||
```
|
||
|
||
**REPL (interactive, geometry stays loaded in memory between queries):**
|
||
```bash
|
||
python step_processor.py enclosure.step --repl
|
||
> list mounting holes
|
||
> bounding box
|
||
> how many M4 holes
|
||
> exit
|
||
```
|
||
|
||
### Query Output Format
|
||
|
||
Always return a structured table:
|
||
```
|
||
MOUNTING HOLES — EnclosureA.step
|
||
─────────────────────────────────────────────────────────────
|
||
# Part Name Diameter Depth Position (x,y,z)
|
||
─────────────────────────────────────────────────────────────
|
||
1 Front Panel 4.20 mm 8.00 mm (12.50, 0.00, 45.20)
|
||
2 Front Panel 4.20 mm 8.00 mm (87.50, 0.00, 45.20)
|
||
─────────────────────────────────────────────────────────────
|
||
Total: 8 holes across 3 parts
|
||
```
|
||
|
||
---
|
||
|
||
## CLI Reference
|
||
|
||
```
|
||
python step_processor.py <file.step> [options]
|
||
|
||
Options:
|
||
--thumbnails Generate 6 PNG views (default: on)
|
||
--no-thumbnails Skip PNG generation
|
||
--bom Export BOM CSV (default: on)
|
||
--no-bom Skip BOM export
|
||
--translate Auto-translate Chinese labels in BOM + produce _EN.step
|
||
--no-translate Skip translation even if Chinese labels detected
|
||
--query "..." Run a single geometric query and exit
|
||
--repl Enter interactive query REPL
|
||
--resolution WxH Thumbnail resolution (default: 1024x768)
|
||
--views front,iso_left,iso_right Comma-separated subset of views
|
||
--verbose Show library selection, fallback notices, timing
|
||
```
|
||
|
||
**Default behavior (no flags):** `--thumbnails --bom` plus auto-detect translation.
|
||
|
||
---
|
||
|
||
## Error Handling
|
||
|
||
| Condition | Behavior |
|
||
|------------------------------------|--------------------------------------------------------|
|
||
| File not found | Exit with clear message, no partial output |
|
||
| Neither library available | Exit with INSTALL.md reference |
|
||
| build123d render fails | Auto-fallback to FreeCAD path, log warning |
|
||
| Translation API unavailable | Write BOM with original names only, note in output |
|
||
| STEP has no assembly structure | Treat as single part, one-row BOM |
|
||
| Chinese detected, no translate flag| Auto-translate by default (skip with --no-translate) |
|
||
| _EN.step entity count mismatch | Abort rewrite, warn, delete partial file |
|
||
| FreeCAD headless GUI module import | Catch ImportError, skip that module, continue |
|
||
|
||
---
|
||
|
||
## INSTALL.md Summary
|
||
|
||
**build123d (primary — native arm64, no conda):**
|
||
```bash
|
||
# 1. Install the arm64 OCP wheel from CadQuery GitHub releases:
|
||
# https://github.com/CadQuery/OCP/releases — pick macosx_*_arm64.whl for your Python
|
||
pip install path/to/cadquery_ocp-*.whl
|
||
|
||
# 2. Install build123d and rendering stack
|
||
pip install build123d trimesh pyrender pillow numpy pandas anthropic
|
||
|
||
# 3. Set API key
|
||
export ANTHROPIC_API_KEY=sk-ant-...
|
||
```
|
||
|
||
**FreeCAD (fallback — native arm64 via conda):**
|
||
```bash
|
||
# Install Miniforge arm64 if not already present:
|
||
# https://github.com/conda-forge/miniforge/releases
|
||
|
||
conda install -c conda-forge freecad
|
||
# Verify headless works:
|
||
FreeCADCmd --version
|
||
```
|
||
|
||
No Rosetta. No x64 sub-environment. Both libraries run natively on Apple Silicon.
|
||
|
||
---
|
||
|
||
## Notes for Claude
|
||
|
||
- Default run = thumbnails + BOM + auto-translate if Chinese detected
|
||
- When a user asks a geometry question, invoke query_engine — don't describe the STEP file from the BOM alone
|
||
- Always confirm output file locations at end of run
|
||
- If Chinese labels found, proactively note it and offer to show the translation map
|
||
- In REPL mode, keep responses tight — it's a terminal session
|
||
- When describing holes or geometry, lead with the table, not prose
|
||
- If build123d fallback fires, tell the user which library ran
|
||
|
||
---
|
||
|
||
## Sub-skill: External Dimensional Diagram
|
||
|
||
### Purpose
|
||
Generates a standardized external dimensional diagram for installation planning,
|
||
product reference, and non-CAD users. NOT a manufacturing drawing.
|
||
Think: installation reference sheet with clean orthographic views + one ISO.
|
||
|
||
### Terminology
|
||
Use: "external dimensional diagram"
|
||
Never use: shop drawing, manufacturing drawing, fabrication drawing
|
||
|
||
### Entry point
|
||
```python
|
||
from modules.external_diagram import step_external_diagram
|
||
|
||
meta = step_external_diagram(
|
||
path="enclosure.step",
|
||
mode="enclosure_only", # or enclosure_plus_mounting, mounting_only
|
||
mapping_file="MR28UW_mapping.json", # optional
|
||
datablock_file="MR28UW.md", # optional, auto-detected if omitted
|
||
options={
|
||
"style": "line_drawing", # or "rendered" — auto if omitted
|
||
"pdf": True, # default False
|
||
"mounting_variant": "Wall Mount", # for enclosure_plus_mounting mode
|
||
"render_variants": True, # generate diagram per mounting variant
|
||
}
|
||
)
|
||
```
|
||
|
||
### Modes
|
||
- `enclosure_only` — enclosure body only (default when no mapping file)
|
||
- `enclosure_plus_mounting` — enclosure + selected mounting subassembly
|
||
- `mounting_only` — mounting geometry only
|
||
|
||
### Styles
|
||
- `line_drawing` — pure SVG wireframe (MR28 reference style). Auto-selected for simple enclosures.
|
||
- `rendered` — rendered ISO views composited with dimensioned orthographic line views
|
||
(MR16 reference style). Auto-selected for complex assemblies (>200 faces).
|
||
|
||
### Layout auto-selection
|
||
- `single_sheet` — default for most models
|
||
- `multi_page` — auto-selected when any dimension > 1200mm or part count > 60
|
||
|
||
### Outputs per run
|
||
| File | Always? | Description |
|
||
|------|---------|-------------|
|
||
| `{stem}__external-diagram.svg` | Yes | Source SVG, always retained |
|
||
| `{stem}__external-diagram.png` | Yes | PNG export via cairosvg |
|
||
| `{stem}__external-diagram.pdf` | Optional (`--pdf` flag) | PDF via cairosvg |
|
||
| `{stem}__meta.json` | Yes | Full metadata per schema |
|
||
| Individual view PNGs | Rendered style only | front, side, rear, iso |
|
||
| Variant diagrams | If `render_variants=True` | One set per mounting variant |
|
||
|
||
### Dimensions shown
|
||
Required: overall width, height, depth
|
||
Auto-detected and shown when found:
|
||
- Active area / screen aperture (with diagonal in mm and inches)
|
||
- Mounting hole chain spacing (horizontal or vertical, auto-selected by readability)
|
||
- VESA pattern if detected
|
||
- Edge offsets to mounting geometry
|
||
Units: metric primary (mm) with imperial in parentheses, smaller italic
|
||
|
||
### Parts mapping file
|
||
JSON file per product model — classifies parts as enclosure/mounting/internal/fastener.
|
||
Drives show/hide logic per mode. Supports mounting variants.
|
||
Schema: `schemas/parts_mapping_schema.json`
|
||
Example: `schemas/parts_mapping_schema.json` → `examples` array
|
||
|
||
### Datablock / title block
|
||
Sourced from a `.md` file alongside the STEP file (auto-detected by matching stem name)
|
||
or passed explicitly as `datablock_file`.
|
||
Template: `templates/datablock_template.md`
|
||
Edit the template to configure: model number, display name, revision, date, company,
|
||
and any custom fields. Custom fields appear in the data block footer.
|
||
|
||
### Metadata schema
|
||
Full JSON schema: `schemas/external_diagram_schema.json`
|
||
Designed for AI consumption, Odoo product data integration, search indexing,
|
||
and CoWork documentation workflows. All geometry in mm.
|
||
|
||
### CLI
|
||
```bash
|
||
# Basic — enclosure only, auto style
|
||
python step_processor.py enclosure.step --diagram
|
||
|
||
# With options
|
||
python step_processor.py enclosure.step --diagram --diagram-mode enclosure_plus_mounting \
|
||
--diagram-style line_drawing --diagram-pdf \
|
||
--mapping mapping/MR28UW_mapping.json \
|
||
--datablock MR28UW.md
|
||
|
||
# Render all mounting variants
|
||
python step_processor.py enclosure.step --diagram --diagram-mode enclosure_plus_mounting \
|
||
--diagram-variants
|
||
```
|
||
|
||
### Notes for Claude
|
||
- When a user says "generate a diagram", "make a dimensional sheet", or "create reference drawing" → invoke this sub-skill
|
||
- Always confirm mode with user if unclear (enclosure_only vs with mounting)
|
||
- If no mapping file exists, default to enclosure_only and note it
|
||
- If multiple mounting variants detected in assembly tree, offer to render each
|
||
- The meta.json output is the primary artifact for downstream AI/workflow consumption
|
||
- cairosvg is required for PNG/PDF export: `pip install cairosvg`
|
||
- Weight/mass fields in meta.json are reserved for future Odoo product data integration
|