feat(skills): OpenSCAD-Skill hinzufuegen
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# OpenSCAD Language Reference (v2021.01)
|
||||
|
||||
## Syntax & Declaration
|
||||
```openscad
|
||||
var = value; // variable assignment
|
||||
var = cond ? value_if_true : value_if_false; // ternary
|
||||
var = function (x) x + x; // function literal
|
||||
module name(params) { ... } // module definition
|
||||
function name(params) = expr; // function definition
|
||||
include <file.scad> // include (executes top-level code)
|
||||
use <file.scad> // import (only modules/functions)
|
||||
```
|
||||
|
||||
## Constants
|
||||
- `undef` — undefined value
|
||||
- `PI` — 3.14159...
|
||||
|
||||
## Special Variables
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `$fn` | Fixed number of segments for circles/spheres |
|
||||
| `$fa` | Minimum angle per segment |
|
||||
| `$fs` | Minimum segment size |
|
||||
| `$t` | Animation step (0–1) |
|
||||
| `$vpr` | Viewport rotation |
|
||||
| `$vpt` | Viewport translation |
|
||||
| `$vpd` | Viewport distance |
|
||||
| `$vpf` | Viewport field of view |
|
||||
| `$children` | Number of child modules |
|
||||
| `$preview` | `true` in preview (F5), `false` in render (F6) |
|
||||
|
||||
## Modifier Characters
|
||||
- `*` — disable (don't render)
|
||||
- `!` — show only this
|
||||
- `#` — highlight/debug (transparent red)
|
||||
- `%` — transparent/background
|
||||
|
||||
## 3D Primitives
|
||||
```openscad
|
||||
cube(size, center=false) // cube([w,d,h]) or cube(s)
|
||||
sphere(r=radius) // sphere(d=diameter)
|
||||
cylinder(h, r=radius, center=false) // cylinder(h, d=diameter)
|
||||
cylinder(h, r1, r2, center=false) // cone/tapered cylinder
|
||||
polyhedron(points, faces, convexity) // custom solid
|
||||
```
|
||||
|
||||
## 2D Primitives
|
||||
```openscad
|
||||
circle(r=radius) // circle(d=diameter)
|
||||
square(size, center=false) // square([w,h])
|
||||
polygon(points, paths) // 2D polygon
|
||||
text(t, size=10, font, halign, valign, spacing) // text string
|
||||
```
|
||||
|
||||
## 2D → 3D Extrusion
|
||||
```openscad
|
||||
linear_extrude(height, center, twist, slices, scale)
|
||||
circle(10); // extrude 2D to 3D
|
||||
|
||||
rotate_extrude(angle=360, convexity)
|
||||
translate([20,0]) circle(5); // lathe/revolve
|
||||
|
||||
surface(file="heightmap.dat", center) // heightmap to 3D
|
||||
```
|
||||
|
||||
## Transformations
|
||||
```openscad
|
||||
translate([x, y, z]) // move
|
||||
rotate([x, y, z]) // rotate (degrees)
|
||||
rotate(angle, [x, y, z]) // rotate around axis
|
||||
scale([x, y, z]) // scale
|
||||
resize([x, y, z], auto) // resize to exact dimensions
|
||||
mirror([x, y, z]) // mirror across plane
|
||||
multmatrix(m) // 4x4 matrix transform
|
||||
color("name", alpha) // color("red"), color("#ff0000")
|
||||
color([r, g, b, a]) // color by RGBA (0-1)
|
||||
offset(r=radius) // 2D offset (round)
|
||||
offset(delta=dist, chamfer=false) // 2D offset (sharp)
|
||||
```
|
||||
|
||||
## Boolean Operations
|
||||
```openscad
|
||||
union() { a(); b(); } // combine (A + B)
|
||||
difference() { a(); b(); } // subtract (A - B)
|
||||
intersection() { a(); b(); } // overlap only (A ∩ B)
|
||||
```
|
||||
|
||||
## Advanced Operations
|
||||
```openscad
|
||||
hull() { a(); b(); } // convex hull
|
||||
minkowski() { a(); b(); } // Minkowski sum (slow!)
|
||||
render(convexity) { ... } // force CGAL render (cache)
|
||||
projection(cut=false) { ... } // 3D → 2D projection
|
||||
```
|
||||
|
||||
## Import / Export
|
||||
```openscad
|
||||
import("file.stl") // import STL, OFF, AMF, 3MF
|
||||
import("file.dxf") // import 2D DXF
|
||||
import("file.svg") // import 2D SVG
|
||||
```
|
||||
|
||||
## Flow Control
|
||||
```openscad
|
||||
if (condition) { ... } else { ... }
|
||||
for (i = [0:10]) { ... } // range [start:end]
|
||||
for (i = [0:2:10]) { ... } // range [start:step:end]
|
||||
for (i = [1, 5, 7]) { ... } // list iteration
|
||||
for (i = [...], j = [...]) { ... } // nested loops
|
||||
intersection_for(i = [...]) { ... } // intersection across iterations
|
||||
let (a = expr) { ... } // local variable scope
|
||||
```
|
||||
|
||||
## List Comprehensions
|
||||
```openscad
|
||||
list = [ for (i = [0:10]) i * 2 ]; // generate
|
||||
list = [ for (i = [0:10]) if (i % 2 == 0) i ]; // filter
|
||||
list = [ for (i = [0:10]) let (x = i*i) x ]; // with let
|
||||
list = [ each sublist ]; // flatten
|
||||
```
|
||||
|
||||
## Math Functions
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `abs(x)` | Absolute value |
|
||||
| `sign(x)` | Sign (-1, 0, 1) |
|
||||
| `sin(x)`, `cos(x)`, `tan(x)` | Trig (degrees) |
|
||||
| `asin(x)`, `acos(x)`, `atan(x)` | Inverse trig |
|
||||
| `atan2(y, x)` | Two-argument arctangent |
|
||||
| `floor(x)`, `ceil(x)`, `round(x)` | Rounding |
|
||||
| `ln(x)`, `log(x)` | Natural / base-10 log |
|
||||
| `pow(base, exp)` | Power |
|
||||
| `sqrt(x)` | Square root |
|
||||
| `exp(x)` | e^x |
|
||||
| `min(a,b,...)`, `max(a,b,...)` | Min/max |
|
||||
| `norm(v)` | Vector magnitude |
|
||||
| `cross(v1, v2)` | Cross product |
|
||||
| `rands(min, max, count)` | Random numbers |
|
||||
| `len(x)` | Length of list/string |
|
||||
|
||||
## String Functions
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `str(...)` | Concatenate to string |
|
||||
| `chr(code)` | ASCII code → character |
|
||||
| `ord(char)` | Character → ASCII code |
|
||||
| `search(needle, haystack)` | Search in list/string |
|
||||
|
||||
## Type Testing
|
||||
```openscad
|
||||
is_undef(x) is_bool(x) is_num(x)
|
||||
is_string(x) is_list(x) is_function(x)
|
||||
```
|
||||
|
||||
## Other Functions
|
||||
```openscad
|
||||
echo("msg", var) // debug output
|
||||
assert(condition, "message") // assertion
|
||||
concat(list1, list2) // concatenate lists
|
||||
lookup(key, [[k,v], ...]) // table lookup
|
||||
children(idx) // access child N of module
|
||||
parent_module(idx) // access parent module name
|
||||
version() // OpenSCAD version string
|
||||
```
|
||||
|
||||
## Operators
|
||||
| Type | Operators |
|
||||
|------|-----------|
|
||||
| Arithmetic | `+` `-` `*` `/` `%` `^` |
|
||||
| Relational | `<` `<=` `==` `!=` `>=` `>` |
|
||||
| Logical | `&&` `\|\|` `!` |
|
||||
| Indexing | `list[i]` `list.x` `list.y` `list.z` |
|
||||
|
||||
## Tips for AI-Generated Code
|
||||
- **Always set `$fn`** for predictable circle resolution
|
||||
- **Use `center=true`** when building symmetric objects with difference()
|
||||
- **Add 0.01mm epsilon** to boolean cuts to avoid coplanar Z-fighting:
|
||||
```openscad
|
||||
difference() {
|
||||
cube([10, 10, 10]);
|
||||
translate([-0.01, 2, 2])
|
||||
cube([10.02, 6, 6]); // slightly larger to ensure clean cut
|
||||
}
|
||||
```
|
||||
- **Modules for reuse**: parameterize everything, compose with modules
|
||||
- **Comments**: explain the "why", dimensions in mm
|
||||
- **Print orientation**: think about which face goes on the build plate
|
||||
- **Manifold check**: all boolean operands must overlap; no coincident faces
|
||||
@@ -0,0 +1,241 @@
|
||||
# STL-to-SCAD Reconstruction Best Practices
|
||||
|
||||
## Profile-Based Reconstruction (Preferred Method)
|
||||
|
||||
For extruded parts (brackets, plates, channels), extract the 2D profile directly from the mesh and convert it to OpenSCAD `polygon()` + `linear_extrude()`. This is more accurate than fitting known primitives.
|
||||
|
||||
### Step 1: Detect Extrusion Axis
|
||||
Use trimesh to find the axis with the most stable cross-section:
|
||||
- Slice the mesh along X, Y, and Z at 64 levels each
|
||||
- For each axis, measure stability: std(area), std(perimeter), std(hole_count)
|
||||
- The axis with lowest stability score is the extrusion axis
|
||||
|
||||
### Step 2: Extract the Dominant Profile
|
||||
- Find the slice with the largest area (the representative cross-section)
|
||||
- Simplify the polygon (remove micro-vertices from tessellation)
|
||||
- Handle holes: inner contours become `paths` in OpenSCAD `polygon()`
|
||||
|
||||
### Step 3: Convert to OpenSCAD
|
||||
```openscad
|
||||
// Auto-generated from mesh profile extraction
|
||||
linear_extrude(height = <extrusion_length>)
|
||||
polygon(
|
||||
points = [<extracted_points>],
|
||||
paths = [<outer_boundary>, <hole_1>, <hole_2>]
|
||||
);
|
||||
```
|
||||
|
||||
### Step 4: Add Secondary Features
|
||||
Features that vary along the extrusion axis (holes, counterbores) are detected by comparing slice profiles at different heights. Where a slice has more holes than the dominant profile, subtract cylinders.
|
||||
|
||||
## The Sculptor Approach (MANDATORY)
|
||||
|
||||
Always model as a sculptor: start from a solid block, then subtract material.
|
||||
|
||||
```openscad
|
||||
// CORRECT: sculptor approach
|
||||
difference() {
|
||||
solid_body(); // 1. Full solid block first
|
||||
channel(); // 2. Subtract channels/slots
|
||||
taper_cuts(); // 3. Subtract wedges/tapers
|
||||
all_holes(); // 4. Subtract ALL holes LAST
|
||||
}
|
||||
|
||||
// WRONG: additive approach (holes get covered)
|
||||
union() {
|
||||
difference() {
|
||||
base();
|
||||
some_holes(); // These get covered by wings!
|
||||
}
|
||||
left_wing(); // Covers the holes above
|
||||
right_wing();
|
||||
}
|
||||
```
|
||||
|
||||
**Why**: In OpenSCAD, `difference()` only applies to its immediate children. If you add material (wings) after cutting holes, the new material covers the holes. The sculptor approach ensures ALL cuts happen after ALL additions.
|
||||
|
||||
## Analysis Pipeline
|
||||
|
||||
### Step 1: Automated SVG Profile Analysis
|
||||
```bash
|
||||
bash openscad-stl-reconstruct.sh model.stl output_dir/
|
||||
```
|
||||
This gives you the 30,000-foot view: dimensions, volume, symmetry, primitive hints.
|
||||
|
||||
### Step 2: Detailed 1mm Z-Slicing
|
||||
For complex models, slice at every 1mm (not just 5 levels):
|
||||
```bash
|
||||
# Generate slices at every Z level
|
||||
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 extract:
|
||||
- Number of contours (1 body = solid level, 2+ = channels/features)
|
||||
- Contour sizes: BODY (>500mm²), FEATURE (50-500mm²), HOLE (<50mm²)
|
||||
- Hole positions from contour centroids
|
||||
- How width changes with Z (reveals taper rate)
|
||||
|
||||
### Step 3: Identify Structure from Profile Data
|
||||
```
|
||||
Z=0-5: 1 body (full width) → Solid base
|
||||
Z=5-10: 2 bodies + 8 holes → Channel appeared, base holes
|
||||
Z=10-20: 2 bodies narrowing → Taper zone (measure rate)
|
||||
Z=20-33: 2 bodies (constant width) → Top section
|
||||
Z=25-27: bodies interrupted by holes → Upper counterbore holes
|
||||
```
|
||||
|
||||
### Step 4: Write OpenSCAD (Sculptor Method)
|
||||
1. Create the FULL solid body (base + wings as one block)
|
||||
2. Subtract the channel
|
||||
3. Subtract taper wedges (use `hull()` for linear tapers)
|
||||
4. Subtract ALL holes in separate modules, called LAST
|
||||
|
||||
### Step 5: Compare and Iterate
|
||||
```bash
|
||||
bash openscad-stl-compare.sh original.stl reconstruction.stl output/
|
||||
```
|
||||
- Check accuracy % (target: >95%)
|
||||
- Read diff images to identify WHAT is wrong
|
||||
- Fix ONE thing per iteration
|
||||
- Re-compare
|
||||
|
||||
## Hole Patterns
|
||||
|
||||
### Counterbore (flat cylindrical pocket)
|
||||
Most common in 3D-printed brackets. A shallow cylinder + through hole:
|
||||
```openscad
|
||||
module counterbore(hole_d, cb_d, cb_depth, total_h) {
|
||||
cylinder(d=hole_d, h=total_h); // Through hole
|
||||
cylinder(d=cb_d, h=cb_depth); // Flat pocket
|
||||
}
|
||||
```
|
||||
|
||||
### Countersink (conical taper)
|
||||
Less common in 3D prints, used for flat-head screws:
|
||||
```openscad
|
||||
module countersink(hole_d, cs_d, cs_depth, total_h) {
|
||||
cylinder(d=hole_d, h=total_h);
|
||||
cylinder(d1=cs_d, d2=hole_d, h=cs_depth);
|
||||
}
|
||||
```
|
||||
|
||||
**Always check reference images** to determine which type is used. Don't assume.
|
||||
|
||||
### Hole Orientation Patterns
|
||||
Complex brackets often have holes on multiple faces:
|
||||
- **Bottom face**: Vertical (Z-axis) holes
|
||||
- **Angled faces**: Holes perpendicular to the face (rotate by taper angle)
|
||||
- **Side walls**: Horizontal (Y-axis) holes
|
||||
- **Each set may have different spacing and count**
|
||||
|
||||
Extract hole positions from SVG centroids at the appropriate Z level.
|
||||
|
||||
## Taper/Wedge Subtraction
|
||||
|
||||
For a 45° taper that narrows a wing from full width to reduced width:
|
||||
```openscad
|
||||
// Wedge: 0 thickness at bottom, full thickness at top
|
||||
hull() {
|
||||
translate([0, outer_edge, z_start])
|
||||
cube([length, eps, z_end - z_start]); // Thin edge
|
||||
translate([0, outer_edge, z_end - eps])
|
||||
cube([length, taper_amount, eps]); // Full face
|
||||
}
|
||||
// Then remove the rectangular block above the taper
|
||||
translate([0, outer_edge, z_end])
|
||||
cube([length, taper_amount, z_top - z_end]);
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **CSG order**: ALWAYS cut holes after building the full solid
|
||||
2. **Feature hallucination**: Don't add features you can't verify in the reference
|
||||
3. **Conical vs cylindrical**: Check if countersinks are tapered or flat
|
||||
4. **Symmetric assumptions**: Don't assume symmetry — verify from SVG data
|
||||
5. **Volume match ≠ shape match**: A model can have correct volume but wrong shape
|
||||
6. **SVG Y-axis is inverted**: OpenSCAD projection flips Y coordinates
|
||||
|
||||
## Dependencies
|
||||
|
||||
```bash
|
||||
pip3 install trimesh numpy scipy rtree shapely
|
||||
brew install admesh
|
||||
```
|
||||
|
||||
## Accuracy Targets
|
||||
|
||||
| Level | Accuracy | When to stop |
|
||||
|-------|----------|-------------|
|
||||
| Draft | >85% | Initial structure verification |
|
||||
| Good | >95% | Functional part, ready for test print |
|
||||
| Excellent | >98% | Production quality |
|
||||
|
||||
The 95% threshold is achievable for most mechanical parts in 4-6 iterations using the SVG profiling approach. The remaining 5% is typically tessellation differences and minor feature details.
|
||||
|
||||
## When to Use Polygon Profiles vs Parametric Primitives
|
||||
|
||||
### Use extracted polygon profiles when:
|
||||
- The shape has mostly flat/angular surfaces (brackets, plates, channels)
|
||||
- The curves are gentle and well-approximated by ~200 polygon points
|
||||
- Speed is more important than last-5% accuracy
|
||||
- Expected accuracy: 75-96% depending on curve complexity
|
||||
|
||||
### Use parametric primitives (circle, cylinder) when:
|
||||
- The shape has prominent cylindrical features (puzzle tabs, screw holes, bosses)
|
||||
- The shape can be decomposed into known primitives (square + circles)
|
||||
- You need >95% accuracy on curved surfaces
|
||||
- The model has symmetry that can be exploited
|
||||
|
||||
### Hybrid approach (best for complex models):
|
||||
1. Extract the polygon profile for the overall outline
|
||||
2. Identify which curves are circles/arcs from the profile data
|
||||
3. Replace polygon approximations with parametric `circle(r)` where possible
|
||||
4. Use `offset(r)` for rounded corners instead of polygon vertices
|
||||
|
||||
### Key lesson: polygon simplification tolerance matters enormously
|
||||
- 0.3mm tolerance → ~50 points → curves become flat → 52% accuracy
|
||||
- 0.05mm tolerance → ~150 points → curves approximate → 75% accuracy
|
||||
- 0.02mm tolerance → ~230 points → curves close but not perfect → 75% accuracy
|
||||
- Diminishing returns beyond ~200 points for polygon-based approaches
|
||||
- For >90% on cylindrical surfaces, parametric primitives are required
|
||||
|
||||
## Feature Hallucination Prevention
|
||||
|
||||
NEVER add features based on visual interpretation of renders alone.
|
||||
- The toothpaste squeezer "cylinder" was actually a rounded slot floor
|
||||
- The puzzle tray "pyramid" didn't exist at all — it was a shadow in the render
|
||||
- ALWAYS verify features with SVG slice data (contour count, area, holes)
|
||||
- If a feature doesn't show as a separate contour in the SVG slices, IT DOESN'T EXIST
|
||||
|
||||
## Adaptive Multi-Axis Slicing
|
||||
|
||||
The `openscad-adaptive-slice.py` script scans STL on all 3 axes:
|
||||
1. Coarse pass (5mm) → detects where cross-section changes
|
||||
2. Fine pass (0.5mm) only at transition zones
|
||||
3. Classifies each zone: solid, shell_or_channel, multi_body, complex
|
||||
|
||||
### How to interpret the feature map
|
||||
|
||||
**Zone types and their OpenSCAD equivalents:**
|
||||
- `solid` (1 contour, 0 holes) → `linear_extrude()` of the profile
|
||||
- `shell_or_channel` (2 contours) → walls around a cavity, use `offset(delta=-wall)`
|
||||
- `solid_with_holes` (1 contour, N holes) → solid body with `difference()` holes
|
||||
- `multi_body` (N contours) → multiple separate parts or holes
|
||||
- `complex` → may need `hull()` between profiles or `polyhedron()`
|
||||
|
||||
**Detecting specific features from zone evolution:**
|
||||
- **Chamfer/taper**: contour width decreases progressively across slices
|
||||
- **Fillet**: smooth curvature in contour centroids between zones
|
||||
- **Counterbore**: nested circular contours with constant radius for several slices
|
||||
- **Through-hole**: hole contour appears in ALL slices along that axis
|
||||
- **Blind hole**: hole contour appears then disappears
|
||||
|
||||
**Generating OpenSCAD from zones:**
|
||||
- Stable zones (many identical slices) → `linear_extrude(height=zone_length)` of representative profile
|
||||
- Transition zones (gradual change) → `hull()` between two profiles at zone boundaries
|
||||
- Feature zones (holes, counterbores) → `difference()` with fitted cylinders
|
||||
|
||||
### Future: Feature Map → OpenSCAD Translator
|
||||
The next evolution is automatic translation: parse the JSON feature map, emit one `module zone_N()` per zone, assemble with `difference()/union()` in the correct order. This would close the loop from STL → analysis → parametric .scad automatically.
|
||||
Reference in New Issue
Block a user