# 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 [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