first commit

This commit is contained in:
2026-07-22 13:48:46 +08:00
commit c87751c3dc
2820 changed files with 726976 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 940 KiB

@@ -0,0 +1,97 @@
# 1 — Twisted hexagonal vase
> **Prompt**
>
> Design a twisted hexagonal vase: a hollow shell about 150 mm tall that tapers from a 70 mm base to a 50 mm mouth, with the hexagonal cross-section twisting 120 degrees from bottom to top, a 2 mm wall, and a closed bottom.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="01-twisted-hex-vase.gif" alt="Twisted hexagonal vase — orbiting render" width="380"></p>
**Parametric controls:** 6 dimensions · 1 colour
**What it demonstrates**
- Generative twist-loft via `linear_extrude(twist=, scale=)` over a hexagon
- Hollowed with a solid printable floor (inner cutter intersected with a lifted cylinder)
- Compensates the hexagon flat-angle so the wall thickness stays true on every face
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `height` | `150` | `[50:5:300]` | Total height of the vase |
| `base_radius` | `35` | `[20:1:100]` | Base radius (circumradius, so diameter is 2x this) |
| `mouth_radius` | `25` | `[10:1:80]` | Mouth radius (circumradius, so diameter is 2x this) |
| `twist_angle` | `120` | `[-720:5:720]` | Total twist angle from bottom to top (degrees) |
| `wall_thickness` | `2.0` | `[0.5:0.5:5.0]` | Thickness of the walls and base |
<details>
<summary>OpenSCAD source — <code>01-twisted-hex-vase.scad</code></summary>
```scad
// Twisted Hexagonal Vase
/* [Dimensions] */
// Total height of the vase
height = 150; // [50:5:300]
// Base radius (circumradius, so diameter is 2x this)
base_radius = 35; // [20:1:100]
// Mouth radius (circumradius, so diameter is 2x this)
mouth_radius = 25; // [10:1:80]
// Total twist angle from bottom to top (degrees)
twist_angle = 120; // [-720:5:720]
// Thickness of the walls and base
wall_thickness = 2.0; // [0.5:0.5:5.0]
/* [Style] */
// Color of the vase
vase_color = "Turquoise";
/* [Hidden] */
// Compensation for the hexagon's angle to maintain true wall thickness on the flats
wall_adj = wall_thickness / cos(30);
// Number of vertical slices for a smooth twist
slices = height * 2;
// Outer geometry variables
r_bot_out = base_radius;
r_top_out = mouth_radius;
scale_out = r_top_out / r_bot_out;
// Inner geometry variables
r_bot_in = base_radius - wall_adj;
r_top_in_at_H = mouth_radius - wall_adj;
// Extend the inner cutter slightly to ensure a clean opening at the top
cut_extra = 2;
H_in = height + cut_extra;
// Calculate slope of inner radius change to maintain perfect alignment
slope = (r_top_in_at_H - r_bot_in) / height;
r_top_in = r_bot_in + slope * H_in;
scale_in = r_top_in / r_bot_in;
twist_in = twist_angle * (H_in / height);
color(vase_color)
difference() {
// Outer Solid Shell
linear_extrude(height = height, twist = twist_angle, scale = scale_out, slices = slices)
circle(r = r_bot_out, $fn = 6);
// Inner Cut (Hollow)
// We intersect the twisted inner solid with a lifted cylinder
// to leave a solid floor of exactly `wall_thickness`.
intersection() {
linear_extrude(height = H_in, twist = twist_in, scale = scale_in, slices = slices)
circle(r = r_bot_in, $fn = 6);
translate([0, 0, wall_thickness])
cylinder(h = H_in, r = max(base_radius, mouth_radius) * 2, $fn = 32);
}
}
```
</details>
@@ -0,0 +1,62 @@
// Twisted Hexagonal Vase
/* [Dimensions] */
// Total height of the vase
height = 150; // [50:5:300]
// Base radius (circumradius, so diameter is 2x this)
base_radius = 35; // [20:1:100]
// Mouth radius (circumradius, so diameter is 2x this)
mouth_radius = 25; // [10:1:80]
// Total twist angle from bottom to top (degrees)
twist_angle = 120; // [-720:5:720]
// Thickness of the walls and base
wall_thickness = 2.0; // [0.5:0.5:5.0]
/* [Style] */
// Color of the vase
vase_color = "Turquoise";
/* [Hidden] */
// Compensation for the hexagon's angle to maintain true wall thickness on the flats
wall_adj = wall_thickness / cos(30);
// Number of vertical slices for a smooth twist
slices = height * 2;
// Outer geometry variables
r_bot_out = base_radius;
r_top_out = mouth_radius;
scale_out = r_top_out / r_bot_out;
// Inner geometry variables
r_bot_in = base_radius - wall_adj;
r_top_in_at_H = mouth_radius - wall_adj;
// Extend the inner cutter slightly to ensure a clean opening at the top
cut_extra = 2;
H_in = height + cut_extra;
// Calculate slope of inner radius change to maintain perfect alignment
slope = (r_top_in_at_H - r_bot_in) / height;
r_top_in = r_bot_in + slope * H_in;
scale_in = r_top_in / r_bot_in;
twist_in = twist_angle * (H_in / height);
color(vase_color)
difference() {
// Outer Solid Shell
linear_extrude(height = height, twist = twist_angle, scale = scale_out, slices = slices)
circle(r = r_bot_out, $fn = 6);
// Inner Cut (Hollow)
// We intersect the twisted inner solid with a lifted cylinder
// to leave a solid floor of exactly `wall_thickness`.
intersection() {
linear_extrude(height = H_in, twist = twist_in, scale = scale_in, slices = slices)
circle(r = r_bot_in, $fn = 6);
translate([0, 0, wall_thickness])
cylinder(h = H_in, r = max(base_radius, mouth_radius) * 2, $fn = 32);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

@@ -0,0 +1,172 @@
# 2 — Knurled control knob
> **Prompt**
>
> Make a knurled control knob 40 mm in diameter and 22 mm tall with a diamond-knurled grip, a raised pointer mark on top, a 6 mm D-shaped shaft bore, and an M3 set-screw hole through the side.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="02-knurled-control-knob.gif" alt="Knurled control knob — orbiting render" width="380"></p>
**Parametric controls:** 15 dimensions · 2 colours
**What it demonstrates**
- Diamond-knurled grip surface (BOSL2)
- D-profile shaft bore + M3 set-screw with counterbore
- Raised pointer indicator, modelled as a separate colour
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `knob_diameter` | `40` | `[20:1:80]` | Outer diameter of the knob |
| `knob_height` | `22` | `[10:1:50]` | Total height of the knob |
| `knurl_density` | `36` | `[16:2:100]` | Number of knurl ridges |
| `knurl_depth` | `1.2` | `[0.5:0.1:3.0]` | Depth of the diamond knurl pattern |
| `shaft_diameter` | `6.0` | `[3:0.1:12]` | Diameter of the D-shaft |
| `shaft_clearance` | `0.2` | `[0:0.05:0.5]` | Extra clearance for easy fit |
| `shaft_flat_depth` | `1.5` | `[0:0.1:4.0]` | Depth of the flat portion on the D-shaft |
| `shaft_bore_depth` | `18` | `[5:1:40]` | How deep the shaft hole goes into the knob |
| `set_screw_diameter` | `3.0` | `[2:0.1:6.0]` | Thread diameter for the set screw (M3 = 3.0) |
| `set_screw_height` | `8` | `[2:1:30]` | Height of the set screw hole from the bottom |
| `set_screw_cb_diameter` | `6.0` | `[3:0.1:10.0]` | Diameter of the counterbore for the set screw head |
| `set_screw_cb_depth` | `12` | `[0:1:30]` | Depth of the counterbore |
| `pointer_angle` | `180` | `[0:15:345]` | Rotational angle of the pointer (180 points opposite to flat) |
| `pointer_width` | `4` | `[2:0.5:10]` | Width of the pointer base |
| … | | | _+1 more_ |
<details>
<summary>OpenSCAD source — <code>02-knurled-control-knob.scad</code></summary>
```scad
include <BOSL2/std.scad>
/* [Dimensions] */
// Outer diameter of the knob
knob_diameter = 40; // [20:1:80]
// Total height of the knob
knob_height = 22; // [10:1:50]
// Number of knurl ridges
knurl_density = 36; // [16:2:100]
// Depth of the diamond knurl pattern
knurl_depth = 1.2; // [0.5:0.1:3.0]
/* [Shaft Interface] */
// Diameter of the D-shaft
shaft_diameter = 6.0; // [3:0.1:12]
// Extra clearance for easy fit
shaft_clearance = 0.2; // [0:0.05:0.5]
// Depth of the flat portion on the D-shaft
shaft_flat_depth = 1.5; // [0:0.1:4.0]
// How deep the shaft hole goes into the knob
shaft_bore_depth = 18; // [5:1:40]
/* [Set Screw] */
// Thread diameter for the set screw (M3 = 3.0)
set_screw_diameter = 3.0; // [2:0.1:6.0]
// Height of the set screw hole from the bottom
set_screw_height = 8; // [2:1:30]
// Diameter of the counterbore for the set screw head
set_screw_cb_diameter = 6.0; // [3:0.1:10.0]
// Depth of the counterbore
set_screw_cb_depth = 12; // [0:1:30]
/* [Pointer] */
// Rotational angle of the pointer (180 points opposite to flat)
pointer_angle = 180; // [0:15:345]
// Width of the pointer base
pointer_width = 4; // [2:0.5:10]
// Raised height of the pointer
pointer_height = 1.5; // [0.5:0.1:5]
/* [Colors] */
knob_color = "DarkSlateGray";
pointer_color = "Silver";
// 2D gear profile for sweeping into knurls
module gear_2d(r, depth, teeth) {
w = ((r - depth) * 2 * 3.14159) / teeth * 1.1;
union() {
circle(r = r - depth, $fn=120);
for(i=[0:teeth-1]) {
rotate([0, 0, i * 360 / teeth])
polygon([
[r - depth + 0.05, -w/2],
[r - depth + 0.05, w/2],
[r, 0]
]);
}
}
}
// Cylindrical base with top and bottom chamfers
module chamfered_cylinder(d, h, chamfer) {
rotate_extrude($fn=120) {
polygon([
[0, 0],
[d/2 - chamfer, 0],
[d/2, chamfer],
[d/2, h - chamfer],
[d/2 - chamfer, h],
[0, h]
]);
}
}
// D-shaft boolean cutter
module d_shaft_cutter(d, depth, flat_dist) {
intersection() {
cylinder(d=d, h=depth, $fn=64);
translate([-d, -d, 0])
cube([d*2, d + flat_dist, depth]);
}
}
// Variables derived from parameters
actual_shaft_d = shaft_diameter + shaft_clearance;
flat_dist = (shaft_diameter/2) - shaft_flat_depth + (shaft_clearance/2);
// Main Assembly
difference() {
// Solid body with diamond knurling
color(knob_color)
intersection() {
twist_angle = 360 * knob_height / (3.14159 * knob_diameter);
intersection() {
linear_extrude(height=knob_height, twist=twist_angle, slices=60, convexity=5)
gear_2d(knob_diameter/2, knurl_depth, knurl_density);
linear_extrude(height=knob_height, twist=-twist_angle, slices=60, convexity=5)
gear_2d(knob_diameter/2, knurl_depth, knurl_density);
}
chamfered_cylinder(knob_diameter, knob_height, 1.5);
}
// D-Shaft Cut
translate([0, 0, -0.05])
d_shaft_cutter(actual_shaft_d, shaft_bore_depth + 0.05, flat_dist);
// Set Screw Cut
translate([0, knob_diameter/2 + 1, set_screw_height])
rotate([90, 0, 0]) {
cylinder(d=set_screw_diameter, h=knob_diameter/2 + 2, $fn=32);
translate([0, 0, -1])
cylinder(d=set_screw_cb_diameter, h=set_screw_cb_depth + 1, $fn=32);
}
}
// Top Pointer Indicator
color(pointer_color)
rotate([0, 0, pointer_angle])
translate([0, 0, knob_height - 0.05])
hull() {
// Outer tip
translate([0, knob_diameter/2 - 4, 0])
cylinder(d=pointer_width/2, h=pointer_height, $fn=32);
// Inner base
translate([0, 4, 0])
cylinder(d=pointer_width, h=pointer_height, $fn=32);
}
```
</details>
@@ -0,0 +1,127 @@
include <BOSL2/std.scad>
/* [Dimensions] */
// Outer diameter of the knob
knob_diameter = 40; // [20:1:80]
// Total height of the knob
knob_height = 22; // [10:1:50]
// Number of knurl ridges
knurl_density = 36; // [16:2:100]
// Depth of the diamond knurl pattern
knurl_depth = 1.2; // [0.5:0.1:3.0]
/* [Shaft Interface] */
// Diameter of the D-shaft
shaft_diameter = 6.0; // [3:0.1:12]
// Extra clearance for easy fit
shaft_clearance = 0.2; // [0:0.05:0.5]
// Depth of the flat portion on the D-shaft
shaft_flat_depth = 1.5; // [0:0.1:4.0]
// How deep the shaft hole goes into the knob
shaft_bore_depth = 18; // [5:1:40]
/* [Set Screw] */
// Thread diameter for the set screw (M3 = 3.0)
set_screw_diameter = 3.0; // [2:0.1:6.0]
// Height of the set screw hole from the bottom
set_screw_height = 8; // [2:1:30]
// Diameter of the counterbore for the set screw head
set_screw_cb_diameter = 6.0; // [3:0.1:10.0]
// Depth of the counterbore
set_screw_cb_depth = 12; // [0:1:30]
/* [Pointer] */
// Rotational angle of the pointer (180 points opposite to flat)
pointer_angle = 180; // [0:15:345]
// Width of the pointer base
pointer_width = 4; // [2:0.5:10]
// Raised height of the pointer
pointer_height = 1.5; // [0.5:0.1:5]
/* [Colors] */
knob_color = "DarkSlateGray";
pointer_color = "Silver";
// 2D gear profile for sweeping into knurls
module gear_2d(r, depth, teeth) {
w = ((r - depth) * 2 * 3.14159) / teeth * 1.1;
union() {
circle(r = r - depth, $fn=120);
for(i=[0:teeth-1]) {
rotate([0, 0, i * 360 / teeth])
polygon([
[r - depth + 0.05, -w/2],
[r - depth + 0.05, w/2],
[r, 0]
]);
}
}
}
// Cylindrical base with top and bottom chamfers
module chamfered_cylinder(d, h, chamfer) {
rotate_extrude($fn=120) {
polygon([
[0, 0],
[d/2 - chamfer, 0],
[d/2, chamfer],
[d/2, h - chamfer],
[d/2 - chamfer, h],
[0, h]
]);
}
}
// D-shaft boolean cutter
module d_shaft_cutter(d, depth, flat_dist) {
intersection() {
cylinder(d=d, h=depth, $fn=64);
translate([-d, -d, 0])
cube([d*2, d + flat_dist, depth]);
}
}
// Variables derived from parameters
actual_shaft_d = shaft_diameter + shaft_clearance;
flat_dist = (shaft_diameter/2) - shaft_flat_depth + (shaft_clearance/2);
// Main Assembly
difference() {
// Solid body with diamond knurling
color(knob_color)
intersection() {
twist_angle = 360 * knob_height / (3.14159 * knob_diameter);
intersection() {
linear_extrude(height=knob_height, twist=twist_angle, slices=60, convexity=5)
gear_2d(knob_diameter/2, knurl_depth, knurl_density);
linear_extrude(height=knob_height, twist=-twist_angle, slices=60, convexity=5)
gear_2d(knob_diameter/2, knurl_depth, knurl_density);
}
chamfered_cylinder(knob_diameter, knob_height, 1.5);
}
// D-Shaft Cut
translate([0, 0, -0.05])
d_shaft_cutter(actual_shaft_d, shaft_bore_depth + 0.05, flat_dist);
// Set Screw Cut
translate([0, knob_diameter/2 + 1, set_screw_height])
rotate([90, 0, 0]) {
cylinder(d=set_screw_diameter, h=knob_diameter/2 + 2, $fn=32);
translate([0, 0, -1])
cylinder(d=set_screw_cb_diameter, h=set_screw_cb_depth + 1, $fn=32);
}
}
// Top Pointer Indicator
color(pointer_color)
rotate([0, 0, pointer_angle])
translate([0, 0, knob_height - 0.05])
hull() {
// Outer tip
translate([0, knob_diameter/2 - 4, 0])
cylinder(d=pointer_width/2, h=pointer_height, $fn=32);
// Inner base
translate([0, 4, 0])
cylinder(d=pointer_width, h=pointer_height, $fn=32);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

@@ -0,0 +1,56 @@
# 3 — Hex bolt & nut — real threads
> **Prompt**
>
> Create an M12 hex bolt 45 mm long with a real threaded shaft and a standard hex head, plus its matching hex nut, placed side by side.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="03-hex-bolt-and-nut.gif" alt="Hex bolt & nut — real threads — orbiting render" width="380"></p>
**Parametric controls:** 3 dimensions · 2 colours
**What it demonstrates**
- **Real ISO metric threads** via BOSL2 `screw()` / `nut()` (spec `"M12x1.75"`) — not faked with stacked cylinders
- A complete fastener set: hex bolt + matching hex nut
- Standard spec string drives pitch, head, and thread profile
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `screw_length` | `45` | `[10:1:100]` | Length of the screw shaft |
<details>
<summary>OpenSCAD source — <code>03-hex-bolt-and-nut.scad</code></summary>
```scad
include <BOSL2/std.scad>
include <BOSL2/screws.scad>
/* [Screw Parameters] */
// The screw size and pitch specification
screw_spec = "M12x1.75";
// Length of the screw shaft
screw_length = 45; // [10:1:100]
/* [Colors] */
bolt_color = "LightSlateGray";
nut_color = "LightSlateGray";
$fn = 64;
// Place bolt on the left
translate([-15, 0, 0])
color(bolt_color)
screw(spec=screw_spec, l=screw_length, head="hex");
// Place nut on the right
translate([15, 0, 0])
color(nut_color)
nut(spec=screw_spec);
```
</details>
@@ -0,0 +1,25 @@
include <BOSL2/std.scad>
include <BOSL2/screws.scad>
/* [Screw Parameters] */
// The screw size and pitch specification
screw_spec = "M12x1.75";
// Length of the screw shaft
screw_length = 45; // [10:1:100]
/* [Colors] */
bolt_color = "LightSlateGray";
nut_color = "LightSlateGray";
$fn = 64;
// Place bolt on the left
translate([-15, 0, 0])
color(bolt_color)
screw(spec=screw_spec, l=screw_length, head="hex");
// Place nut on the right
translate([15, 0, 0])
color(nut_color)
nut(spec=screw_spec);
Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

@@ -0,0 +1,182 @@
# 4 — Honeycomb lightweight bracket
> **Prompt**
>
> Design a 90-degree angle mounting bracket with 80x80 mm flanges that are 5 mm thick, lightened with a hexagonal honeycomb cutout pattern on both faces, four M5 mounting holes, and a filleted inside corner.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="04-honeycomb-bracket.gif" alt="Honeycomb lightweight bracket — orbiting render" width="380"></p>
**Parametric controls:** 13 dimensions · 1 colour
**What it demonstrates**
- Generative hexagonal honeycomb lightening pattern, kept manifold with a solid border frame
- 90° L-bracket with a filleted inner corner for strength
- Four M5 mounting holes
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `flange_length` | `80` | `[40:1:150]` | Length of each flange from the outer corner |
| `bracket_width` | `80` | `[40:1:150]` | Width of the bracket |
| `thickness` | `5` | `[2:1:15]` | Thickness of the flanges |
| `corner_fillet` | `5` | `[1:1:15]` | Radius of the inner corner fillet |
| `hole_dia` | `5.2` | `[2:0.1:10]` | Diameter of the mounting holes (M5 clearance) |
| `hole_boss_dia` | `16` | `[8:1:30]` | Solid boundary diameter around each hole |
| `hole_offset_x` | `45` | `[10:1:100]` | Hole X position (distance from outer corner) |
| `hole_offset_y1` | `20` | `[10:1:100]` | Hole Y1 position (distance from side edge) |
| `hole_offset_y2` | `60` | `[10:1:100]` | Hole Y2 position (distance from side edge) |
| `hex_flat_to_flat` | `12` | `[5:1:30]` | Flat-to-flat inner diameter of each hexagon |
| `hex_wall` | `3` | `[1:0.5:10]` | Wall thickness between hexagons |
| `border_thickness` | `6` | `[2:1:20]` | Solid border thickness around the edges |
| `bracket_color` | `"#4682B4"` | `color` | |
<details>
<summary>OpenSCAD source — <code>04-honeycomb-bracket.scad</code></summary>
```scad
// 90-Degree Mounting Bracket with Honeycomb
/* [Dimensions] */
// Length of each flange from the outer corner
flange_length = 80; // [40:1:150]
// Width of the bracket
bracket_width = 80; // [40:1:150]
// Thickness of the flanges
thickness = 5; // [2:1:15]
// Radius of the inner corner fillet
corner_fillet = 5; // [1:1:15]
/* [Mounting Holes] */
// Diameter of the mounting holes (M5 clearance)
hole_dia = 5.2; // [2:0.1:10]
// Solid boundary diameter around each hole
hole_boss_dia = 16; // [8:1:30]
// Hole X position (distance from outer corner)
hole_offset_x = 45; // [10:1:100]
// Hole Y1 position (distance from side edge)
hole_offset_y1 = 20; // [10:1:100]
// Hole Y2 position (distance from side edge)
hole_offset_y2 = 60; // [10:1:100]
/* [Honeycomb Pattern] */
// Flat-to-flat inner diameter of each hexagon
hex_flat_to_flat = 12; // [5:1:30]
// Wall thickness between hexagons
hex_wall = 3; // [1:0.5:10]
// Solid border thickness around the edges
border_thickness = 6; // [2:1:20]
/* [Rendering] */
bracket_color = "#4682B4"; // color
$fn = 64;
// Combine hole positions into an array (4 holes total, 2 per flange)
hole_positions = [
[hole_offset_x, hole_offset_y1],
[hole_offset_x, hole_offset_y2]
];
// Helper module to generate a 2D grid of hexagons
module hex_grid(w, h, d, wall) {
S = d + wall;
dx = S;
dy = S * sin(60);
cols = ceil(w / dx) + 1;
rows = ceil(h / dy) + 1;
for (i = [-1 : cols]) {
for (j = [-1 : rows]) {
x = i * dx + (j % 2) * (dx / 2);
y = j * dy;
translate([x, y]) rotate(30) circle(d = d / cos(30), $fn=6);
}
}
}
// Generates the 2D masking area to keep borders and bosses solid
module honeycomb_mask() {
difference() {
// Safe inner zone
translate([thickness + corner_fillet + border_thickness, border_thickness])
square([flange_length - (thickness + corner_fillet + border_thickness * 2),
bracket_width - border_thickness * 2]);
// Subtract bosses around the mounting holes
for (pos = hole_positions) {
translate(pos) circle(d = hole_boss_dia);
}
}
}
// The final 2D honeycomb cutout pattern
module honeycomb_cut_2d() {
intersection() {
// Generate full grid slightly larger than needed to ensure coverage
translate([-10, -10]) hex_grid(flange_length + 20, bracket_width + 20, hex_flat_to_flat, hex_wall);
honeycomb_mask();
}
}
// 2D L-profile of the bracket
module bracket_profile() {
R_in = corner_fillet;
R_out = thickness + corner_fillet;
C = [thickness + corner_fillet, thickness + corner_fillet];
union() {
// Flange 1 right of corner
translate([C.x, 0]) square([flange_length - C.x, thickness]);
// Flange 2 above corner
translate([0, C.y]) square([thickness, flange_length - C.y]);
// Filleted Corner section
intersection() {
square([C.x, C.y]);
translate(C) difference() {
circle(r = R_out);
circle(r = R_in);
}
}
}
}
// Build the final 3D part
color(bracket_color)
difference() {
// 1. Base solid bracket (L-extrusion)
translate([0, bracket_width, 0])
rotate([90, 0, 0])
linear_extrude(bracket_width)
bracket_profile();
// 2. Flange 1 holes (Z-axis)
for (pos = hole_positions) {
translate([pos.x, pos.y, -1])
cylinder(d = hole_dia, h = thickness + 2);
}
// 3. Flange 2 holes (X-axis)
for (pos = hole_positions) {
translate([-1, pos.y, pos.x])
rotate([0, 90, 0])
cylinder(d = hole_dia, h = thickness + 2);
}
// 4. Flange 1 honeycomb cutout
translate([0, 0, -1])
linear_extrude(height = thickness + 2)
honeycomb_cut_2d();
// 5. Flange 2 honeycomb cutout
translate([thickness + 1, 0, 0])
rotate([0, -90, 0])
linear_extrude(height = thickness + 2)
honeycomb_cut_2d();
}
```
</details>
@@ -0,0 +1,139 @@
// 90-Degree Mounting Bracket with Honeycomb
/* [Dimensions] */
// Length of each flange from the outer corner
flange_length = 80; // [40:1:150]
// Width of the bracket
bracket_width = 80; // [40:1:150]
// Thickness of the flanges
thickness = 5; // [2:1:15]
// Radius of the inner corner fillet
corner_fillet = 5; // [1:1:15]
/* [Mounting Holes] */
// Diameter of the mounting holes (M5 clearance)
hole_dia = 5.2; // [2:0.1:10]
// Solid boundary diameter around each hole
hole_boss_dia = 16; // [8:1:30]
// Hole X position (distance from outer corner)
hole_offset_x = 45; // [10:1:100]
// Hole Y1 position (distance from side edge)
hole_offset_y1 = 20; // [10:1:100]
// Hole Y2 position (distance from side edge)
hole_offset_y2 = 60; // [10:1:100]
/* [Honeycomb Pattern] */
// Flat-to-flat inner diameter of each hexagon
hex_flat_to_flat = 12; // [5:1:30]
// Wall thickness between hexagons
hex_wall = 3; // [1:0.5:10]
// Solid border thickness around the edges
border_thickness = 6; // [2:1:20]
/* [Rendering] */
bracket_color = "#4682B4"; // color
$fn = 64;
// Combine hole positions into an array (4 holes total, 2 per flange)
hole_positions = [
[hole_offset_x, hole_offset_y1],
[hole_offset_x, hole_offset_y2]
];
// Helper module to generate a 2D grid of hexagons
module hex_grid(w, h, d, wall) {
S = d + wall;
dx = S;
dy = S * sin(60);
cols = ceil(w / dx) + 1;
rows = ceil(h / dy) + 1;
for (i = [-1 : cols]) {
for (j = [-1 : rows]) {
x = i * dx + (j % 2) * (dx / 2);
y = j * dy;
translate([x, y]) rotate(30) circle(d = d / cos(30), $fn=6);
}
}
}
// Generates the 2D masking area to keep borders and bosses solid
module honeycomb_mask() {
difference() {
// Safe inner zone
translate([thickness + corner_fillet + border_thickness, border_thickness])
square([flange_length - (thickness + corner_fillet + border_thickness * 2),
bracket_width - border_thickness * 2]);
// Subtract bosses around the mounting holes
for (pos = hole_positions) {
translate(pos) circle(d = hole_boss_dia);
}
}
}
// The final 2D honeycomb cutout pattern
module honeycomb_cut_2d() {
intersection() {
// Generate full grid slightly larger than needed to ensure coverage
translate([-10, -10]) hex_grid(flange_length + 20, bracket_width + 20, hex_flat_to_flat, hex_wall);
honeycomb_mask();
}
}
// 2D L-profile of the bracket
module bracket_profile() {
R_in = corner_fillet;
R_out = thickness + corner_fillet;
C = [thickness + corner_fillet, thickness + corner_fillet];
union() {
// Flange 1 right of corner
translate([C.x, 0]) square([flange_length - C.x, thickness]);
// Flange 2 above corner
translate([0, C.y]) square([thickness, flange_length - C.y]);
// Filleted Corner section
intersection() {
square([C.x, C.y]);
translate(C) difference() {
circle(r = R_out);
circle(r = R_in);
}
}
}
}
// Build the final 3D part
color(bracket_color)
difference() {
// 1. Base solid bracket (L-extrusion)
translate([0, bracket_width, 0])
rotate([90, 0, 0])
linear_extrude(bracket_width)
bracket_profile();
// 2. Flange 1 holes (Z-axis)
for (pos = hole_positions) {
translate([pos.x, pos.y, -1])
cylinder(d = hole_dia, h = thickness + 2);
}
// 3. Flange 2 holes (X-axis)
for (pos = hole_positions) {
translate([-1, pos.y, pos.x])
rotate([0, 90, 0])
cylinder(d = hole_dia, h = thickness + 2);
}
// 4. Flange 1 honeycomb cutout
translate([0, 0, -1])
linear_extrude(height = thickness + 2)
honeycomb_cut_2d();
// 5. Flange 2 honeycomb cutout
translate([thickness + 1, 0, 0])
rotate([0, -90, 0])
linear_extrude(height = thickness + 2)
honeycomb_cut_2d();
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

@@ -0,0 +1,168 @@
# 5 — NACA 2412 tapered wing
> **Prompt**
>
> Model a tapered aircraft wing section using a real NACA 2412 airfoil: 120 mm root chord tapering to 80 mm tip over a 200 mm span, with two spanwise spar tubes and a few lightening holes.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="05-naca-airfoil-wing.gif" alt="NACA 2412 tapered wing — orbiting render" width="380"></p>
**Parametric controls:** 9 dimensions · 1 colour
**What it demonstrates**
- A true **NACA 4-digit airfoil** built from the thickness + camber equations (cosine-spaced points)
- Tapered loft from a 120 mm root chord to an 80 mm tip over the span
- Spanwise spar bores and a row of lightening holes
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `root_chord` | `120` | `[50:10:300]` | Tapered Wing Section Parameters |
| `tip_chord` | `80` | `[30:10:200]` | |
| `span` | `200` | `[50:10:500]` | |
| `naca_m` | `0.02` | `[0.00:0.01:0.09]` | First digit (max camber in hundredths) |
| `naca_p` | `0.40` | `[0.10:0.10:0.90]` | Second digit (position of max camber in tenths) |
| `naca_t` | `0.12` | `[0.05:0.01:0.30]` | Last two digits (max thickness in hundredths) |
| `spar_radius` | `4` | `[1:0.5:10]` | |
| `lightening_holes` | `5` | `[0:1:10]` | |
| `wing_color` | `"SteelBlue"` | `[SteelBlue, Silver, Orange, White, DimGray]` | |
<details>
<summary>OpenSCAD source — <code>05-naca-airfoil-wing.scad</code></summary>
```scad
// Tapered Wing Section Parameters
/* [Wing Geometry] */
root_chord = 120; // [50:10:300]
tip_chord = 80; // [30:10:200]
span = 200; // [50:10:500]
/* [NACA 4-Digit Profile] */
// First digit (max camber in hundredths)
naca_m = 0.02; // [0.00:0.01:0.09]
// Second digit (position of max camber in tenths)
naca_p = 0.40; // [0.10:0.10:0.90]
// Last two digits (max thickness in hundredths)
naca_t = 0.12; // [0.05:0.01:0.30]
/* [Structural Features] */
spar_radius = 4; // [1:0.5:10]
lightening_holes = 5; // [0:1:10]
/* [Appearance] */
wing_color = "SteelBlue"; // [SteelBlue, Silver, Orange, White, DimGray]
$fn = 64;
// --- Mathematical Functions for NACA 4-Digit Airfoil ---
function naca_camber(x, m, p) =
(m == 0 || p == 0) ? 0 :
(x < p) ? (m / pow(p,2)) * (2 * p * x - pow(x,2))
: (m / pow(1-p,2)) * ((1 - 2*p) + 2 * p * x - pow(x,2));
function naca_camber_deriv(x, m, p) =
(m == 0 || p == 0) ? 0 :
(x < p) ? (2 * m / pow(p,2)) * (p - x)
: (2 * m / pow(1-p,2)) * (p - x);
function naca_points(m, p, t, N=80) =
let (
upper = [for (i=[0:N])
let (
x = 0.5 * (1 - cos(i * 180 / N)),
yt = 5 * t * (0.2969 * sqrt(max(0, x)) - 0.1260 * x - 0.3516 * pow(x,2) + 0.2843 * pow(x,3) - 0.1015 * pow(x,4)),
yc = naca_camber(x, m, p),
dyc = naca_camber_deriv(x, m, p),
theta = atan(dyc)
)
[x - yt * sin(theta), yc + yt * cos(theta)]
],
lower = [for (i=[N-1:-1:1]) // Skip duplicate endpoints to form a closed loop
let (
x = 0.5 * (1 - cos(i * 180 / N)),
yt = 5 * t * (0.2969 * sqrt(max(0, x)) - 0.1260 * x - 0.3516 * pow(x,2) + 0.2843 * pow(x,3) - 0.1015 * pow(x,4)),
yc = naca_camber(x, m, p),
dyc = naca_camber_deriv(x, m, p),
theta = atan(dyc)
)
[x + yt * sin(theta), yc - yt * cos(theta)]
]
)
concat(upper, lower);
// --- Modules ---
// Generates a rounded-end cylinder between two points, safely extended to cut cleanly
module strut(p1, p2, r) {
v = p2 - p1;
ext = 5;
dir = v / norm(v);
p1_ext = p1 - dir * ext;
p2_ext = p2 + dir * ext;
hull() {
translate(p1_ext) sphere(r=r);
translate(p2_ext) sphere(r=r);
}
}
// --- Main Geometry Assembly ---
// Calculate standard spar locations (25% and 70% of chord length)
x_spar1 = 0.25;
x_spar2 = 0.70;
// Calculate 3D coordinates for the spar tubes to correctly follow the camber and taper
p1_root = [x_spar1 * root_chord, naca_camber(x_spar1, naca_m, naca_p) * root_chord, 0];
p1_tip = [x_spar1 * tip_chord, naca_camber(x_spar1, naca_m, naca_p) * tip_chord, span];
p2_root = [x_spar2 * root_chord, naca_camber(x_spar2, naca_m, naca_p) * root_chord, 0];
p2_tip = [x_spar2 * tip_chord, naca_camber(x_spar2, naca_m, naca_p) * tip_chord, span];
// Rotate wing to lay "flat" for standard viewing
// (Span extends along +Y, chord along +X, thickness aligns with Z)
color(wing_color)
rotate([-90, 0, 0])
difference() {
// Solid Wing Shape
linear_extrude(height = span, scale = tip_chord / root_chord)
scale([root_chord, root_chord])
polygon(naca_points(naca_m, naca_p, naca_t));
// Spanwise Spar Tube Cuts
strut(p1_root, p1_tip, spar_radius);
strut(p2_root, p2_tip, spar_radius);
// Lightening Hole Cuts (spaced evenly along span)
if (lightening_holes > 0) {
for (i = [1 : lightening_holes]) {
let (
// Space holes along the span
z_pos = i * span / (lightening_holes + 1),
// Determine the chord length at this spanwise position
local_chord = root_chord + (tip_chord - root_chord) * (z_pos / span),
// Position halfway between the two spars
x_pos = 0.475 * local_chord,
// Make the hole size proportional to the local chord length
hole_r = local_chord * 0.12
)
// Cut through the airfoil thickness (Y axis in the unrotated frame)
translate([x_pos, 0, z_pos])
rotate([90, 0, 0])
cylinder(h=root_chord * 2, r=hole_r, center=true);
}
}
}
```
</details>
@@ -0,0 +1,129 @@
// Tapered Wing Section Parameters
/* [Wing Geometry] */
root_chord = 120; // [50:10:300]
tip_chord = 80; // [30:10:200]
span = 200; // [50:10:500]
/* [NACA 4-Digit Profile] */
// First digit (max camber in hundredths)
naca_m = 0.02; // [0.00:0.01:0.09]
// Second digit (position of max camber in tenths)
naca_p = 0.40; // [0.10:0.10:0.90]
// Last two digits (max thickness in hundredths)
naca_t = 0.12; // [0.05:0.01:0.30]
/* [Structural Features] */
spar_radius = 4; // [1:0.5:10]
lightening_holes = 5; // [0:1:10]
/* [Appearance] */
wing_color = "SteelBlue"; // [SteelBlue, Silver, Orange, White, DimGray]
$fn = 64;
// --- Mathematical Functions for NACA 4-Digit Airfoil ---
function naca_camber(x, m, p) =
(m == 0 || p == 0) ? 0 :
(x < p) ? (m / pow(p,2)) * (2 * p * x - pow(x,2))
: (m / pow(1-p,2)) * ((1 - 2*p) + 2 * p * x - pow(x,2));
function naca_camber_deriv(x, m, p) =
(m == 0 || p == 0) ? 0 :
(x < p) ? (2 * m / pow(p,2)) * (p - x)
: (2 * m / pow(1-p,2)) * (p - x);
function naca_points(m, p, t, N=80) =
let (
upper = [for (i=[0:N])
let (
x = 0.5 * (1 - cos(i * 180 / N)),
yt = 5 * t * (0.2969 * sqrt(max(0, x)) - 0.1260 * x - 0.3516 * pow(x,2) + 0.2843 * pow(x,3) - 0.1015 * pow(x,4)),
yc = naca_camber(x, m, p),
dyc = naca_camber_deriv(x, m, p),
theta = atan(dyc)
)
[x - yt * sin(theta), yc + yt * cos(theta)]
],
lower = [for (i=[N-1:-1:1]) // Skip duplicate endpoints to form a closed loop
let (
x = 0.5 * (1 - cos(i * 180 / N)),
yt = 5 * t * (0.2969 * sqrt(max(0, x)) - 0.1260 * x - 0.3516 * pow(x,2) + 0.2843 * pow(x,3) - 0.1015 * pow(x,4)),
yc = naca_camber(x, m, p),
dyc = naca_camber_deriv(x, m, p),
theta = atan(dyc)
)
[x + yt * sin(theta), yc - yt * cos(theta)]
]
)
concat(upper, lower);
// --- Modules ---
// Generates a rounded-end cylinder between two points, safely extended to cut cleanly
module strut(p1, p2, r) {
v = p2 - p1;
ext = 5;
dir = v / norm(v);
p1_ext = p1 - dir * ext;
p2_ext = p2 + dir * ext;
hull() {
translate(p1_ext) sphere(r=r);
translate(p2_ext) sphere(r=r);
}
}
// --- Main Geometry Assembly ---
// Calculate standard spar locations (25% and 70% of chord length)
x_spar1 = 0.25;
x_spar2 = 0.70;
// Calculate 3D coordinates for the spar tubes to correctly follow the camber and taper
p1_root = [x_spar1 * root_chord, naca_camber(x_spar1, naca_m, naca_p) * root_chord, 0];
p1_tip = [x_spar1 * tip_chord, naca_camber(x_spar1, naca_m, naca_p) * tip_chord, span];
p2_root = [x_spar2 * root_chord, naca_camber(x_spar2, naca_m, naca_p) * root_chord, 0];
p2_tip = [x_spar2 * tip_chord, naca_camber(x_spar2, naca_m, naca_p) * tip_chord, span];
// Rotate wing to lay "flat" for standard viewing
// (Span extends along +Y, chord along +X, thickness aligns with Z)
color(wing_color)
rotate([-90, 0, 0])
difference() {
// Solid Wing Shape
linear_extrude(height = span, scale = tip_chord / root_chord)
scale([root_chord, root_chord])
polygon(naca_points(naca_m, naca_p, naca_t));
// Spanwise Spar Tube Cuts
strut(p1_root, p1_tip, spar_radius);
strut(p2_root, p2_tip, spar_radius);
// Lightening Hole Cuts (spaced evenly along span)
if (lightening_holes > 0) {
for (i = [1 : lightening_holes]) {
let (
// Space holes along the span
z_pos = i * span / (lightening_holes + 1),
// Determine the chord length at this spanwise position
local_chord = root_chord + (tip_chord - root_chord) * (z_pos / span),
// Position halfway between the two spars
x_pos = 0.475 * local_chord,
// Make the hole size proportional to the local chord length
hole_r = local_chord * 0.12
)
// Cut through the airfoil thickness (Y axis in the unrotated frame)
translate([x_pos, 0, z_pos])
rotate([90, 0, 0])
cylinder(h=root_chord * 2, r=hole_r, center=true);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

@@ -0,0 +1,115 @@
# 6 — Threaded jar & screw-on lid
> **Prompt**
>
> Create a small storage jar with external screw threads at the neck and a matching screw-on lid with internal threads. Jar body 60 mm diameter, 70 mm tall, 2.5 mm walls; show the lid unscrewed and sitting beside the jar.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="06-threaded-jar-and-lid.gif" alt="Threaded jar & screw-on lid — orbiting render" width="380"></p>
**Parametric controls:** 9 dimensions · 2 colours
**What it demonstrates**
- Two **mating** threaded parts that share a pitch and diameter (external neck thread + internal lid thread) via BOSL2 threading
- Hollow jar body with printable walls
- Lid with a fluted grip rim, shown unscrewed beside the jar
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `jar_diameter` | `60` | `[20:1:100]` | |
| `jar_height` | `70` | `[20:1:200]` | |
| `wall_thickness` | `2.5` | `[1:0.1:5]` | |
| `neck_diameter` | `55` | `[20:1:100]` | |
| `neck_height` | `15` | `[5:1:30]` | |
| `thread_pitch` | `3` | `[1:0.5:5]` | |
| `lid_clearance` | `0.5` | `[0:0.1:2]` | |
<details>
<summary>OpenSCAD source — <code>06-threaded-jar-and-lid.scad</code></summary>
```scad
include <BOSL2/std.scad>
include <BOSL2/threading.scad>
/* [Jar Dimensions] */
jar_diameter = 60; // [20:1:100]
jar_height = 70; // [20:1:200]
wall_thickness = 2.5; // [1:0.1:5]
/* [Neck and Threading] */
neck_diameter = 55; // [20:1:100]
neck_height = 15; // [5:1:30]
thread_pitch = 3; // [1:0.5:5]
lid_clearance = 0.5; // [0:0.1:2]
/* [Colors] */
jar_color = "LightSkyBlue";
lid_color = "SteelBlue";
$fn = 64;
shoulder_height = 5;
body_height = jar_height - neck_height - shoulder_height;
// Position jar
translate([-35, 0, 0])
color(jar_color)
difference() {
// Solid exterior
union() {
// Body
cylinder(d=jar_diameter, h=body_height);
// Shoulder
translate([0, 0, body_height])
cylinder(d1=jar_diameter, d2=neck_diameter, h=shoulder_height);
// Neck
translate([0, 0, body_height + shoulder_height])
threaded_rod(d=neck_diameter, l=neck_height, pitch=thread_pitch, anchor=BOTTOM);
}
// Hollow interior
union() {
// Body interior
translate([0, 0, wall_thickness])
cylinder(d=jar_diameter - 2 * wall_thickness, h=body_height - wall_thickness + 0.01);
// Shoulder interior
translate([0, 0, body_height])
cylinder(d1=jar_diameter - 2 * wall_thickness, d2=neck_diameter - 2 * wall_thickness, h=shoulder_height + 0.01);
// Neck interior
translate([0, 0, body_height + shoulder_height])
cylinder(d=neck_diameter - 2 * wall_thickness, h=neck_height + 1);
}
}
// Position lid beside the jar, upside down to show internal threads
translate([35, 0, neck_height + wall_thickness])
rotate([180, 0, 0])
color(lid_color)
difference() {
lid_outer_d = neck_diameter + 2 * wall_thickness + 2 * lid_clearance + 2;
// Lid exterior with grips
difference() {
cylinder(d=lid_outer_d, h=neck_height + wall_thickness);
// Grips
for (i = [0 : 15 : 359]) {
rotate([0, 0, i])
translate([lid_outer_d/2, 0, -1])
cylinder(r=1.5, h=neck_height + wall_thickness + 2, $fn=16);
}
}
// Internal threads cutout
translate([0, 0, -0.1])
threaded_rod(d=neck_diameter + lid_clearance, l=neck_height + 0.2, pitch=thread_pitch, anchor=BOTTOM, internal=true);
}
```
</details>
@@ -0,0 +1,78 @@
include <BOSL2/std.scad>
include <BOSL2/threading.scad>
/* [Jar Dimensions] */
jar_diameter = 60; // [20:1:100]
jar_height = 70; // [20:1:200]
wall_thickness = 2.5; // [1:0.1:5]
/* [Neck and Threading] */
neck_diameter = 55; // [20:1:100]
neck_height = 15; // [5:1:30]
thread_pitch = 3; // [1:0.5:5]
lid_clearance = 0.5; // [0:0.1:2]
/* [Colors] */
jar_color = "LightSkyBlue";
lid_color = "SteelBlue";
$fn = 64;
shoulder_height = 5;
body_height = jar_height - neck_height - shoulder_height;
// Position jar
translate([-35, 0, 0])
color(jar_color)
difference() {
// Solid exterior
union() {
// Body
cylinder(d=jar_diameter, h=body_height);
// Shoulder
translate([0, 0, body_height])
cylinder(d1=jar_diameter, d2=neck_diameter, h=shoulder_height);
// Neck
translate([0, 0, body_height + shoulder_height])
threaded_rod(d=neck_diameter, l=neck_height, pitch=thread_pitch, anchor=BOTTOM);
}
// Hollow interior
union() {
// Body interior
translate([0, 0, wall_thickness])
cylinder(d=jar_diameter - 2 * wall_thickness, h=body_height - wall_thickness + 0.01);
// Shoulder interior
translate([0, 0, body_height])
cylinder(d1=jar_diameter - 2 * wall_thickness, d2=neck_diameter - 2 * wall_thickness, h=shoulder_height + 0.01);
// Neck interior
translate([0, 0, body_height + shoulder_height])
cylinder(d=neck_diameter - 2 * wall_thickness, h=neck_height + 1);
}
}
// Position lid beside the jar, upside down to show internal threads
translate([35, 0, neck_height + wall_thickness])
rotate([180, 0, 0])
color(lid_color)
difference() {
lid_outer_d = neck_diameter + 2 * wall_thickness + 2 * lid_clearance + 2;
// Lid exterior with grips
difference() {
cylinder(d=lid_outer_d, h=neck_height + wall_thickness);
// Grips
for (i = [0 : 15 : 359]) {
rotate([0, 0, i])
translate([lid_outer_d/2, 0, -1])
cylinder(r=1.5, h=neck_height + wall_thickness + 2, $fn=16);
}
}
// Internal threads cutout
translate([0, 0, -0.1])
threaded_rod(d=neck_diameter + lid_clearance, l=neck_height + 0.2, pitch=thread_pitch, anchor=BOTTOM, internal=true);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

@@ -0,0 +1,98 @@
# 7 — Right-angle bevel gear drive
> **Prompt**
>
> Build a right-angle bevel gear drive: a 24-tooth bevel gear on a vertical shaft meshing at 90 degrees with a 16-tooth bevel pinion on a horizontal shaft, each on a short stub shaft.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="07-bevel-gear-drive.gif" alt="Right-angle bevel gear drive — orbiting render" width="380"></p>
**Parametric controls:** 9 dimensions · 3 colours
**What it demonstrates**
- A meshing **bevel gear pair** at 90° via BOSL2 `bevel_gear()` with matched pitch cones
- 24-tooth gear + 16-tooth pinion, each on a stub shaft
- Three colours for gear / pinion / shafts
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `gear1_teeth` | `24` | `[10:1:60]` | |
| `gear2_teeth` | `16` | `[10:1:60]` | |
| `circular_pitch` | `5` | `[2:0.5:10]` | |
| `face_width` | `8` | `[4:1:20]` | |
| `bore_diameter` | `5` | `[2:0.5:20]` | |
| `shaft_length` | `30` | `[10:1:50]` | |
| `shaft_diameter` | `5` | `[2:0.5:20]` | |
| `gear2_spin` | `11.25` | `[0:0.1:45]` | |
<details>
<summary>OpenSCAD source — <code>07-bevel-gear-drive.scad</code></summary>
```scad
include <BOSL2/std.scad>
include <BOSL2/gears.scad>
/* [Gear Specifications] */
gear1_teeth = 24; // [10:1:60]
gear2_teeth = 16; // [10:1:60]
circular_pitch = 5; // [2:0.5:10]
face_width = 8; // [4:1:20]
bore_diameter = 5; // [2:0.5:20]
shaft_length = 30; // [10:1:50]
shaft_diameter = 5; // [2:0.5:20]
gear2_spin = 11.25; // [0:0.1:45]
/* [Colors] */
gear1_color = "SteelBlue";
gear2_color = "DarkOrange";
shaft_color = "Silver";
$fn = 64;
module gear_system() {
// Gear 1: Vertical (Z-axis)
color(gear1_color)
bevel_gear(
circ_pitch = circular_pitch,
teeth = gear1_teeth,
mate_teeth = gear2_teeth,
face_width = face_width,
bore = bore_diameter,
spiral = 0,
anchor = "apex"
);
// Vertical Shaft
color(shaft_color)
translate([0, 0, -shaft_length/2])
cylinder(h=shaft_length, d=shaft_diameter, center=true);
// Gear 2: Horizontal (X-axis)
color(gear2_color)
rotate([0, 90, 0])
rotate([0, 0, gear2_spin]) // Fine-tune tooth meshing
bevel_gear(
circ_pitch = circular_pitch,
teeth = gear2_teeth,
mate_teeth = gear1_teeth,
face_width = face_width,
bore = bore_diameter,
spiral = 0,
anchor = "apex"
);
// Horizontal Shaft
color(shaft_color)
rotate([0, 90, 0])
translate([0, 0, -shaft_length/2])
cylinder(h=shaft_length, d=shaft_diameter, center=true);
}
gear_system();
```
</details>
@@ -0,0 +1,60 @@
include <BOSL2/std.scad>
include <BOSL2/gears.scad>
/* [Gear Specifications] */
gear1_teeth = 24; // [10:1:60]
gear2_teeth = 16; // [10:1:60]
circular_pitch = 5; // [2:0.5:10]
face_width = 8; // [4:1:20]
bore_diameter = 5; // [2:0.5:20]
shaft_length = 30; // [10:1:50]
shaft_diameter = 5; // [2:0.5:20]
gear2_spin = 11.25; // [0:0.1:45]
/* [Colors] */
gear1_color = "SteelBlue";
gear2_color = "DarkOrange";
shaft_color = "Silver";
$fn = 64;
module gear_system() {
// Gear 1: Vertical (Z-axis)
color(gear1_color)
bevel_gear(
circ_pitch = circular_pitch,
teeth = gear1_teeth,
mate_teeth = gear2_teeth,
face_width = face_width,
bore = bore_diameter,
spiral = 0,
anchor = "apex"
);
// Vertical Shaft
color(shaft_color)
translate([0, 0, -shaft_length/2])
cylinder(h=shaft_length, d=shaft_diameter, center=true);
// Gear 2: Horizontal (X-axis)
color(gear2_color)
rotate([0, 90, 0])
rotate([0, 0, gear2_spin]) // Fine-tune tooth meshing
bevel_gear(
circ_pitch = circular_pitch,
teeth = gear2_teeth,
mate_teeth = gear1_teeth,
face_width = face_width,
bore = bore_diameter,
spiral = 0,
anchor = "apex"
);
// Horizontal Shaft
color(shaft_color)
rotate([0, 90, 0])
translate([0, 0, -shaft_length/2])
cylinder(h=shaft_length, d=shaft_diameter, center=true);
}
gear_system();
Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

@@ -0,0 +1,123 @@
# 8 — Centrifugal pump impeller
> **Prompt**
>
> Design a centrifugal pump impeller: a 90 mm diameter back-plate with a central 12 mm bore and a raised hub, and seven backward-curved blades that sweep from the hub out to the rim, each blade curving smoothly along its path.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="08-centrifugal-impeller.gif" alt="Centrifugal pump impeller — orbiting render" width="380"></p>
**Parametric controls:** 10 dimensions · 1 colour
**What it demonstrates**
- Seven **backward-curved blades** swept along curved paths (BOSL2) in a clean radial array
- Raised central hub with a 12 mm bore on a circular back-plate
- Smooth turbomachinery surfaces, fused manifold to the plate
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `impeller_diameter` | `90` | `[50:1:150]` | Outer diameter of the impeller back-plate |
| `bore_diameter` | `12` | `[4:0.5:30]` | Diameter of the central bore hole |
| `backplate_thickness` | `3` | `[1:0.5:10]` | Thickness of the main back-plate |
| `hub_diameter` | `26` | `[15:1:50]` | Outer diameter of the raised central hub |
| `hub_height` | `20` | `[10:1:60]` | Total height of the hub from the bottom of the back-plate |
| `blade_count` | `7` | `[3:1:15]` | Number of backward-curved blades |
| `blade_height` | `12` | `[2:1:40]` | Height of the blades above the back-plate |
| `blade_thickness` | `3` | `[1:0.5:8]` | Thickness of each blade |
| `sweep_angle` | `75` | `[10:1:180]` | The angle (in degrees) that the blades sweep backwards |
| `part_color` | `"#B0C4DE"` | `LightSteelBlue` | |
| `steps` | `48` | `Curve resolution` | |
<details>
<summary>OpenSCAD source — <code>08-centrifugal-impeller.scad</code></summary>
```scad
include <BOSL2/std.scad>
/* [Main Dimensions] */
// Outer diameter of the impeller back-plate
impeller_diameter = 90; // [50:1:150]
// Diameter of the central bore hole
bore_diameter = 12; // [4:0.5:30]
// Thickness of the main back-plate
backplate_thickness = 3; // [1:0.5:10]
/* [Hub Dimensions] */
// Outer diameter of the raised central hub
hub_diameter = 26; // [15:1:50]
// Total height of the hub from the bottom of the back-plate
hub_height = 20; // [10:1:60]
/* [Blade Parameters] */
// Number of backward-curved blades
blade_count = 7; // [3:1:15]
// Height of the blades above the back-plate
blade_height = 12; // [2:1:40]
// Thickness of each blade
blade_thickness = 3; // [1:0.5:8]
// The angle (in degrees) that the blades sweep backwards
sweep_angle = 75; // [10:1:180]
/* [Appearance] */
part_color = "#B0C4DE"; // LightSteelBlue
$fn = 64;
module centrifugal_impeller() {
difference() {
union() {
// Back-plate
cyl(d=impeller_diameter, l=backplate_thickness, chamfer1=1, anchor=BOTTOM);
// Raised Hub
cyl(d=hub_diameter, l=hub_height, chamfer2=1.5, anchor=BOTTOM);
// Blades (logarithmic spiral for constant incidence angle)
// Inner radius overlaps the hub slightly to ensure a solid union
blade_inner_r = (hub_diameter / 2) - 0.5;
// Outer radius stops slightly short of the rim so the round endcaps don't overhang
blade_outer_r = (impeller_diameter / 2) - (blade_thickness / 2);
steps = 48; // Curve resolution
// Calculate the 2D path of the sweeping blade
pts = [
for(i = [0 : steps])
let(
t = i / steps,
r = blade_inner_r * pow(blade_outer_r / blade_inner_r, t),
a = -sweep_angle * t
)
[r * cos(a), r * sin(a)]
];
// Distribute and extrude the 7 blades
for(i = [0 : blade_count - 1]) {
zrot(i * 360 / blade_count)
up(backplate_thickness)
linear_extrude(height = blade_height)
stroke(pts, width=blade_thickness, endcaps="round");
}
}
// Central Bore (subtracted at the end to ensure a clean hole through hub and blades)
down(1)
cyl(d=bore_diameter, l=hub_height + 2, anchor=BOTTOM);
}
}
color(part_color)
centrifugal_impeller();
```
</details>
@@ -0,0 +1,82 @@
include <BOSL2/std.scad>
/* [Main Dimensions] */
// Outer diameter of the impeller back-plate
impeller_diameter = 90; // [50:1:150]
// Diameter of the central bore hole
bore_diameter = 12; // [4:0.5:30]
// Thickness of the main back-plate
backplate_thickness = 3; // [1:0.5:10]
/* [Hub Dimensions] */
// Outer diameter of the raised central hub
hub_diameter = 26; // [15:1:50]
// Total height of the hub from the bottom of the back-plate
hub_height = 20; // [10:1:60]
/* [Blade Parameters] */
// Number of backward-curved blades
blade_count = 7; // [3:1:15]
// Height of the blades above the back-plate
blade_height = 12; // [2:1:40]
// Thickness of each blade
blade_thickness = 3; // [1:0.5:8]
// The angle (in degrees) that the blades sweep backwards
sweep_angle = 75; // [10:1:180]
/* [Appearance] */
part_color = "#B0C4DE"; // LightSteelBlue
$fn = 64;
module centrifugal_impeller() {
difference() {
union() {
// Back-plate
cyl(d=impeller_diameter, l=backplate_thickness, chamfer1=1, anchor=BOTTOM);
// Raised Hub
cyl(d=hub_diameter, l=hub_height, chamfer2=1.5, anchor=BOTTOM);
// Blades (logarithmic spiral for constant incidence angle)
// Inner radius overlaps the hub slightly to ensure a solid union
blade_inner_r = (hub_diameter / 2) - 0.5;
// Outer radius stops slightly short of the rim so the round endcaps don't overhang
blade_outer_r = (impeller_diameter / 2) - (blade_thickness / 2);
steps = 48; // Curve resolution
// Calculate the 2D path of the sweeping blade
pts = [
for(i = [0 : steps])
let(
t = i / steps,
r = blade_inner_r * pow(blade_outer_r / blade_inner_r, t),
a = -sweep_angle * t
)
[r * cos(a), r * sin(a)]
];
// Distribute and extrude the 7 blades
for(i = [0 : blade_count - 1]) {
zrot(i * 360 / blade_count)
up(backplate_thickness)
linear_extrude(height = blade_height)
stroke(pts, width=blade_thickness, endcaps="round");
}
}
// Central Bore (subtracted at the end to ensure a clean hole through hub and blades)
down(1)
cyl(d=bore_diameter, l=hub_height + 2, anchor=BOTTOM);
}
}
color(part_color)
centrifugal_impeller();
Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

@@ -0,0 +1,190 @@
# 9 — Herringbone planetary gear stage
> **Prompt**
>
> Model a herringbone planetary gear stage at module 1.5: a central sun gear with 18 teeth, three planet gears with 18 teeth each meshing around it, an internal ring gear with 54 teeth, and a carrier plate linking the three planet axles. Color the sun, planets, ring, and carrier differently.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="09-herringbone-planetary-gearbox.gif" alt="Herringbone planetary gear stage — orbiting render" width="380"></p>
**Parametric controls:** 10 dimensions · 4 colours
**What it demonstrates**
- A full **epicyclic assembly**: sun gear + three planets + an internal ring gear + a carrier plate
- **Herringbone** teeth on every gear; ring teeth = sun + 2 × planet (54)
- Four distinct colours for sun / planets / ring / carrier
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `gear_module` | `1.5` | `[1:0.1:3]` | |
| `gear_thickness` | `10` | `[5:1:20]` | |
| `sun_twist` | `10` | `[0:1:30]` | |
| `sun_bore` | `6` | `[3:0.5:10]` | |
| `planet_bore` | `6` | `[3:0.5:10]` | |
<details>
<summary>OpenSCAD source — <code>09-herringbone-planetary-gearbox.scad</code></summary>
```scad
// Title: Herringbone Planetary Gear Stage
// Description: A robust, manifold module 1.5 planetary gear stage with herringbone teeth, carrier plate, and custom colors.
// Version: v4
use <MCAD/involute_gears.scad>
PI_VAL = 3.141592653589793;
/* [Gear Parameters] */
gear_module = 1.5; // [1:0.1:3]
gear_thickness = 10; // [5:1:20]
sun_twist = 10; // [0:1:30]
sun_bore = 6; // [3:0.5:10]
planet_bore = 6; // [3:0.5:10]
/* [Colors] */
sun_color = "Gold";
planet_color = "Tomato";
ring_color = "SteelBlue";
carrier_color = "Silver";
/* [Calculated Constants] */
sun_teeth = 18;
planet_teeth = 18;
ring_teeth = 54;
pitch_sun = gear_module * sun_teeth / 2;
pitch_planet = gear_module * planet_teeth / 2;
pitch_ring = gear_module * ring_teeth / 2;
center_distance = pitch_sun + pitch_planet;
$fn = 64;
// ==============================
// Modules
// ==============================
module single_gear(teeth, mod, half_t, t_angle) {
gear(
number_of_teeth = teeth,
circular_pitch = PI_VAL * mod,
pressure_angle = 20,
clearance = 0.25,
gear_thickness = half_t,
rim_thickness = half_t,
hub_thickness = half_t,
bore_diameter = 0,
twist = t_angle
);
}
module herringbone_gear_base(mod, teeth, thickness, base_twist, is_planet) {
half_t = thickness / 2;
// Maintain constant helix angle across different gear sizes
t_angle = (base_twist * 18 / teeth) * (is_planet ? -1 : 1);
union() {
single_gear(teeth, mod, half_t, t_angle);
translate([0, 0, thickness])
mirror([0, 0, 1])
single_gear(teeth, mod, half_t, t_angle);
}
}
module hb_gear(teeth, is_planet=false, bore=0) {
difference() {
herringbone_gear_base(gear_module, teeth, gear_thickness, sun_twist, is_planet);
if (bore > 0) {
translate([0, 0, -1])
cylinder(r=bore/2, h=gear_thickness+2, $fn=32);
}
}
}
module ring_gear() {
outer_radius = pitch_ring + 4 * gear_module;
// Shift slightly to prevent Z-fighting at top and bottom
translate([0, 0, 0.01])
difference() {
union() {
cylinder(r=outer_radius, h=gear_thickness - 0.02, $fn=128);
// Outer grip ridges
for(i=[0:10:359]) {
rotate([0, 0, i])
translate([outer_radius, 0, 0])
cylinder(r=gear_module, h=gear_thickness - 0.02, $fn=16);
}
}
// Subtract a slightly scaled herringbone gear to form the internal teeth with clearance
translate([0, 0, -0.01])
scale([1.015, 1.015, 1])
rotate([0, 0, 360 / ring_teeth / 2]) // half tooth phase shift
herringbone_gear_base(gear_module, ring_teeth, gear_thickness, sun_twist, is_planet=true);
}
}
module carrier() {
// Carrier plate positioned slightly above the gears
translate([0, 0, gear_thickness + 0.5])
difference() {
union() {
// Main carrier disc
cylinder(r=center_distance + 8, h=3, $fn=64);
// Pins connecting the planet gears
for(i=[0:120:359]) {
rotate([0, 0, i])
translate([center_distance, 0, -gear_thickness - 0.1])
cylinder(r=planet_bore/2 - 0.2, h=gear_thickness + 1.1, $fn=32);
}
// Central pin acting as sun bearing
translate([0, 0, -gear_thickness - 0.1])
cylinder(r=sun_bore/2 - 0.2, h=gear_thickness + 1.1, $fn=32);
}
// Aesthetic weight-saving cutouts
for(i=[0:60:359]) {
rotate([0, 0, i + 30])
translate([center_distance * 0.55, 0, -1])
cylinder(r=6, h=5, $fn=32);
}
// Central hole through the carrier shaft
translate([0, 0, -gear_thickness - 0.2])
cylinder(r=sun_bore/2 - 1.5, h=gear_thickness + 5, $fn=16);
}
}
// ==============================
// Assembly
// ==============================
// Central Sun Gear
color(sun_color)
hb_gear(sun_teeth, is_planet=false, bore=sun_bore);
// Planet Gears (x3)
color(planet_color)
for(i=[0:120:359]) {
rotate([0, 0, i])
translate([center_distance, 0, 0])
rotate([0, 0, 360 / planet_teeth / 2]) // Phase shift by half tooth for perfect meshing
hb_gear(planet_teeth, is_planet=true, bore=planet_bore);
}
// Internal Ring Gear
color(ring_color)
ring_gear();
// Carrier Plate
color(carrier_color)
carrier();
```
</details>
@@ -0,0 +1,155 @@
// Title: Herringbone Planetary Gear Stage
// Description: A robust, manifold module 1.5 planetary gear stage with herringbone teeth, carrier plate, and custom colors.
// Version: v4
use <MCAD/involute_gears.scad>
PI_VAL = 3.141592653589793;
/* [Gear Parameters] */
gear_module = 1.5; // [1:0.1:3]
gear_thickness = 10; // [5:1:20]
sun_twist = 10; // [0:1:30]
sun_bore = 6; // [3:0.5:10]
planet_bore = 6; // [3:0.5:10]
/* [Colors] */
sun_color = "Gold";
planet_color = "Tomato";
ring_color = "SteelBlue";
carrier_color = "Silver";
/* [Calculated Constants] */
sun_teeth = 18;
planet_teeth = 18;
ring_teeth = 54;
pitch_sun = gear_module * sun_teeth / 2;
pitch_planet = gear_module * planet_teeth / 2;
pitch_ring = gear_module * ring_teeth / 2;
center_distance = pitch_sun + pitch_planet;
$fn = 64;
// ==============================
// Modules
// ==============================
module single_gear(teeth, mod, half_t, t_angle) {
gear(
number_of_teeth = teeth,
circular_pitch = PI_VAL * mod,
pressure_angle = 20,
clearance = 0.25,
gear_thickness = half_t,
rim_thickness = half_t,
hub_thickness = half_t,
bore_diameter = 0,
twist = t_angle
);
}
module herringbone_gear_base(mod, teeth, thickness, base_twist, is_planet) {
half_t = thickness / 2;
// Maintain constant helix angle across different gear sizes
t_angle = (base_twist * 18 / teeth) * (is_planet ? -1 : 1);
union() {
single_gear(teeth, mod, half_t, t_angle);
translate([0, 0, thickness])
mirror([0, 0, 1])
single_gear(teeth, mod, half_t, t_angle);
}
}
module hb_gear(teeth, is_planet=false, bore=0) {
difference() {
herringbone_gear_base(gear_module, teeth, gear_thickness, sun_twist, is_planet);
if (bore > 0) {
translate([0, 0, -1])
cylinder(r=bore/2, h=gear_thickness+2, $fn=32);
}
}
}
module ring_gear() {
outer_radius = pitch_ring + 4 * gear_module;
// Shift slightly to prevent Z-fighting at top and bottom
translate([0, 0, 0.01])
difference() {
union() {
cylinder(r=outer_radius, h=gear_thickness - 0.02, $fn=128);
// Outer grip ridges
for(i=[0:10:359]) {
rotate([0, 0, i])
translate([outer_radius, 0, 0])
cylinder(r=gear_module, h=gear_thickness - 0.02, $fn=16);
}
}
// Subtract a slightly scaled herringbone gear to form the internal teeth with clearance
translate([0, 0, -0.01])
scale([1.015, 1.015, 1])
rotate([0, 0, 360 / ring_teeth / 2]) // half tooth phase shift
herringbone_gear_base(gear_module, ring_teeth, gear_thickness, sun_twist, is_planet=true);
}
}
module carrier() {
// Carrier plate positioned slightly above the gears
translate([0, 0, gear_thickness + 0.5])
difference() {
union() {
// Main carrier disc
cylinder(r=center_distance + 8, h=3, $fn=64);
// Pins connecting the planet gears
for(i=[0:120:359]) {
rotate([0, 0, i])
translate([center_distance, 0, -gear_thickness - 0.1])
cylinder(r=planet_bore/2 - 0.2, h=gear_thickness + 1.1, $fn=32);
}
// Central pin acting as sun bearing
translate([0, 0, -gear_thickness - 0.1])
cylinder(r=sun_bore/2 - 0.2, h=gear_thickness + 1.1, $fn=32);
}
// Aesthetic weight-saving cutouts
for(i=[0:60:359]) {
rotate([0, 0, i + 30])
translate([center_distance * 0.55, 0, -1])
cylinder(r=6, h=5, $fn=32);
}
// Central hole through the carrier shaft
translate([0, 0, -gear_thickness - 0.2])
cylinder(r=sun_bore/2 - 1.5, h=gear_thickness + 5, $fn=16);
}
}
// ==============================
// Assembly
// ==============================
// Central Sun Gear
color(sun_color)
hb_gear(sun_teeth, is_planet=false, bore=sun_bore);
// Planet Gears (x3)
color(planet_color)
for(i=[0:120:359]) {
rotate([0, 0, i])
translate([center_distance, 0, 0])
rotate([0, 0, 360 / planet_teeth / 2]) // Phase shift by half tooth for perfect meshing
hb_gear(planet_teeth, is_planet=true, bore=planet_bore);
}
// Internal Ring Gear
color(ring_color)
ring_gear();
// Carrier Plate
color(carrier_color)
carrier();
Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

@@ -0,0 +1,229 @@
# 10 — 9-cylinder radial aircraft engine
> **Prompt**
>
> Design a 9-cylinder radial aircraft engine: a central round crankcase with nine finned cylinders arranged evenly in a star pattern around it, each cylinder with stacked cooling fins and a domed cylinder head, and a central propeller shaft hub at the front.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="10-radial-aircraft-engine.gif" alt="9-cylinder radial aircraft engine — orbiting render" width="380"></p>
**Parametric controls:** 15 dimensions · 6 colours
**What it demonstrates**
- Nine finned cylinders in a radial star, generated as a pattern around a crankcase
- Stacked cooling fins, domed heads, and a central propeller hub
- 15 parametric controls across 6 distinct part colours
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `num_cylinders` | `9` | `[3:1:18]` | |
| `crankcase_diameter` | `100` | `[50:5:150]` | |
| `crankcase_thickness` | `40` | `[20:5:80]` | |
| `hub_diameter` | `30` | `[10:2:60]` | |
| `hub_length` | `35` | `[10:5:80]` | |
| `shaft_diameter` | `12` | `[5:1:30]` | |
| `shaft_length` | `40` | `[10:5:100]` | |
| `cylinder_diameter` | `22` | `[10:2:50]` | |
| `cylinder_length` | `60` | `[30:5:120]` | |
| `fin_thickness` | `1.2` | `[0.5:0.1:3]` | |
| `fin_spacing` | `3.5` | `[1.5:0.5:10]` | |
| `fin_overhang` | `3` | `[1:0.5:10]` | |
| `pushrod_diameter` | `3` | `[1:0.5:6]` | |
| `intake_diameter` | `4` | `[1:0.5:8]` | |
<details>
<summary>OpenSCAD source — <code>10-radial-aircraft-engine.scad</code></summary>
```scad
/* [Engine Core] */
num_cylinders = 9; // [3:1:18]
crankcase_diameter = 100; // [50:5:150]
crankcase_thickness = 40; // [20:5:80]
hub_diameter = 30; // [10:2:60]
hub_length = 35; // [10:5:80]
shaft_diameter = 12; // [5:1:30]
shaft_length = 40; // [10:5:100]
/* [Cylinders] */
cylinder_diameter = 22; // [10:2:50]
cylinder_length = 60; // [30:5:120]
fin_thickness = 1.2; // [0.5:0.1:3]
fin_spacing = 3.5; // [1.5:0.5:10]
fin_overhang = 3; // [1:0.5:10]
/* [Details] */
pushrod_diameter = 3; // [1:0.5:6]
intake_diameter = 4; // [1:0.5:8]
/* [Colors] */
crankcase_color = "SlateGray";
barrel_color = "Silver";
head_color = "DimGray";
pushrod_color = "LightGray";
intake_color = "Gray";
shaft_color = "Silver";
$fn = 64;
module engine() {
// Crankcase
color(crankcase_color) {
// Main body
cylinder(d=crankcase_diameter, h=crankcase_thickness, center=true);
// Chamfer rings to soften the drum edges
translate([0, 0, crankcase_thickness/2])
rotate_extrude() translate([crankcase_diameter/2, 0, 0]) circle(d=4);
translate([0, 0, -crankcase_thickness/2])
rotate_extrude() translate([crankcase_diameter/2, 0, 0]) circle(d=4);
// Front reduction gear housing
translate([0, 0, crankcase_thickness/2])
cylinder(d1=crankcase_diameter * 0.85, d2=hub_diameter * 1.5, h=12);
translate([0, 0, crankcase_thickness/2 + 12])
cylinder(d1=hub_diameter * 1.5, d2=hub_diameter * 1.2, h=hub_length - 12);
// Bearing housing cap
translate([0, 0, crankcase_thickness/2 + hub_length])
cylinder(d=hub_diameter * 1.25, h=5);
// Rear accessory section
translate([0, 0, -crankcase_thickness/2]) rotate([180, 0, 0])
cylinder(d1=crankcase_diameter * 0.85, d2=crankcase_diameter * 0.6, h=25);
// Crankcase ribs/bolts details
for(i=[0:num_cylinders-1]) {
rotate([0, 0, i * 360/num_cylinders + 180/num_cylinders])
translate([crankcase_diameter/2, 0, 0])
cylinder(d=8, h=crankcase_thickness, center=true);
}
}
// Prop Shaft
color(shaft_color) {
translate([0, 0, crankcase_thickness/2 + hub_length]) {
cylinder(d=shaft_diameter, h=shaft_length);
// Propeller mounting flange
translate([0, 0, shaft_length * 0.3]) {
difference() {
cylinder(d=shaft_diameter * 2.5, h=4);
// Bolt holes
for(b=[0:5]) {
rotate([0, 0, b * 360/6])
translate([shaft_diameter * 0.9, 0, -1])
cylinder(d=2, h=6);
}
}
}
translate([0, 0, shaft_length * 0.3 + 4])
cylinder(d=shaft_diameter * 1.5, h=shaft_length * 0.1);
}
}
// Cylinders and Rods
for(i=[0:num_cylinders-1]) {
angle = i * 360 / num_cylinders;
rotate([0, 0, angle]) {
// Cylinder
translate([crankcase_diameter/2 - 5, 0, 0])
rotate([0, 90, 0])
radial_cylinder();
// Pushrods (Front)
pushrod_base_x = crankcase_diameter * 0.31;
pushrod_base_z = crankcase_thickness/2 + 6;
pushrod_top_x = crankcase_diameter/2 - 5 + cylinder_length * 0.9;
pushrod_top_y = cylinder_diameter * 0.25;
pushrod_top_z = 0;
draw_rod([pushrod_base_x, -pushrod_top_y, pushrod_base_z],
[pushrod_top_x, -pushrod_top_y, pushrod_top_z],
pushrod_diameter, pushrod_color);
draw_rod([pushrod_base_x, pushrod_top_y, pushrod_base_z],
[pushrod_top_x, pushrod_top_y, pushrod_top_z],
pushrod_diameter, pushrod_color);
// Intake pipe (Back)
intake_base_x = crankcase_diameter * 0.37;
intake_base_z = -crankcase_thickness/2 - 8;
intake_top_x = crankcase_diameter/2 - 5 + cylinder_length * 0.85;
intake_top_y = 0;
intake_top_z = -cylinder_diameter * 0.4;
draw_rod([intake_base_x, 0, intake_base_z],
[intake_top_x, 0, intake_top_z],
intake_diameter, intake_color);
}
}
}
module radial_cylinder() {
// Barrel
color(barrel_color) {
// Core
cylinder(d=cylinder_diameter, h=cylinder_length * 0.7);
// Fins
num_fins = floor((cylinder_length * 0.65) / fin_spacing);
for(f=[1:num_fins]) {
translate([0, 0, f * fin_spacing + 2])
cylinder(d=cylinder_diameter + 2 * fin_overhang, h=fin_thickness, center=true);
}
}
// Head
color(head_color) {
translate([0, 0, cylinder_length * 0.7]) {
// Lower head block
cylinder(d=cylinder_diameter + 2, h=cylinder_length * 0.2);
// Domed top
translate([0, 0, cylinder_length * 0.2])
scale([1, 1, 0.7]) sphere(d=cylinder_diameter + 2);
// Rocker boxes
translate([0, cylinder_diameter * 0.25, cylinder_length * 0.2])
rotate([90, 0, 0])
cylinder(d=cylinder_diameter * 0.5, h=cylinder_diameter * 0.5, center=true);
translate([0, -cylinder_diameter * 0.25, cylinder_length * 0.2])
rotate([90, 0, 0])
cylinder(d=cylinder_diameter * 0.5, h=cylinder_diameter * 0.5, center=true);
// Head fins
head_fins = 3;
for(f=[1:head_fins]) {
translate([0, 0, f * fin_spacing - 1])
cylinder(d=cylinder_diameter + 4, h=fin_thickness, center=true);
}
}
}
}
module draw_rod(p1, p2, dia, rod_color) {
color(rod_color) {
dist = norm(p2 - p1);
dir = (p2 - p1) / dist;
axis = cross([0,0,1], dir);
angle = acos(max(-1, min(1, dir[2])));
translate(p1)
if (norm(axis) > 0.001) {
rotate(a=angle, v=axis)
cylinder(d=dia, h=dist);
} else {
cylinder(d=dia, h=dist);
}
}
}
engine();
```
</details>
@@ -0,0 +1,185 @@
/* [Engine Core] */
num_cylinders = 9; // [3:1:18]
crankcase_diameter = 100; // [50:5:150]
crankcase_thickness = 40; // [20:5:80]
hub_diameter = 30; // [10:2:60]
hub_length = 35; // [10:5:80]
shaft_diameter = 12; // [5:1:30]
shaft_length = 40; // [10:5:100]
/* [Cylinders] */
cylinder_diameter = 22; // [10:2:50]
cylinder_length = 60; // [30:5:120]
fin_thickness = 1.2; // [0.5:0.1:3]
fin_spacing = 3.5; // [1.5:0.5:10]
fin_overhang = 3; // [1:0.5:10]
/* [Details] */
pushrod_diameter = 3; // [1:0.5:6]
intake_diameter = 4; // [1:0.5:8]
/* [Colors] */
crankcase_color = "SlateGray";
barrel_color = "Silver";
head_color = "DimGray";
pushrod_color = "LightGray";
intake_color = "Gray";
shaft_color = "Silver";
$fn = 64;
module engine() {
// Crankcase
color(crankcase_color) {
// Main body
cylinder(d=crankcase_diameter, h=crankcase_thickness, center=true);
// Chamfer rings to soften the drum edges
translate([0, 0, crankcase_thickness/2])
rotate_extrude() translate([crankcase_diameter/2, 0, 0]) circle(d=4);
translate([0, 0, -crankcase_thickness/2])
rotate_extrude() translate([crankcase_diameter/2, 0, 0]) circle(d=4);
// Front reduction gear housing
translate([0, 0, crankcase_thickness/2])
cylinder(d1=crankcase_diameter * 0.85, d2=hub_diameter * 1.5, h=12);
translate([0, 0, crankcase_thickness/2 + 12])
cylinder(d1=hub_diameter * 1.5, d2=hub_diameter * 1.2, h=hub_length - 12);
// Bearing housing cap
translate([0, 0, crankcase_thickness/2 + hub_length])
cylinder(d=hub_diameter * 1.25, h=5);
// Rear accessory section
translate([0, 0, -crankcase_thickness/2]) rotate([180, 0, 0])
cylinder(d1=crankcase_diameter * 0.85, d2=crankcase_diameter * 0.6, h=25);
// Crankcase ribs/bolts details
for(i=[0:num_cylinders-1]) {
rotate([0, 0, i * 360/num_cylinders + 180/num_cylinders])
translate([crankcase_diameter/2, 0, 0])
cylinder(d=8, h=crankcase_thickness, center=true);
}
}
// Prop Shaft
color(shaft_color) {
translate([0, 0, crankcase_thickness/2 + hub_length]) {
cylinder(d=shaft_diameter, h=shaft_length);
// Propeller mounting flange
translate([0, 0, shaft_length * 0.3]) {
difference() {
cylinder(d=shaft_diameter * 2.5, h=4);
// Bolt holes
for(b=[0:5]) {
rotate([0, 0, b * 360/6])
translate([shaft_diameter * 0.9, 0, -1])
cylinder(d=2, h=6);
}
}
}
translate([0, 0, shaft_length * 0.3 + 4])
cylinder(d=shaft_diameter * 1.5, h=shaft_length * 0.1);
}
}
// Cylinders and Rods
for(i=[0:num_cylinders-1]) {
angle = i * 360 / num_cylinders;
rotate([0, 0, angle]) {
// Cylinder
translate([crankcase_diameter/2 - 5, 0, 0])
rotate([0, 90, 0])
radial_cylinder();
// Pushrods (Front)
pushrod_base_x = crankcase_diameter * 0.31;
pushrod_base_z = crankcase_thickness/2 + 6;
pushrod_top_x = crankcase_diameter/2 - 5 + cylinder_length * 0.9;
pushrod_top_y = cylinder_diameter * 0.25;
pushrod_top_z = 0;
draw_rod([pushrod_base_x, -pushrod_top_y, pushrod_base_z],
[pushrod_top_x, -pushrod_top_y, pushrod_top_z],
pushrod_diameter, pushrod_color);
draw_rod([pushrod_base_x, pushrod_top_y, pushrod_base_z],
[pushrod_top_x, pushrod_top_y, pushrod_top_z],
pushrod_diameter, pushrod_color);
// Intake pipe (Back)
intake_base_x = crankcase_diameter * 0.37;
intake_base_z = -crankcase_thickness/2 - 8;
intake_top_x = crankcase_diameter/2 - 5 + cylinder_length * 0.85;
intake_top_y = 0;
intake_top_z = -cylinder_diameter * 0.4;
draw_rod([intake_base_x, 0, intake_base_z],
[intake_top_x, 0, intake_top_z],
intake_diameter, intake_color);
}
}
}
module radial_cylinder() {
// Barrel
color(barrel_color) {
// Core
cylinder(d=cylinder_diameter, h=cylinder_length * 0.7);
// Fins
num_fins = floor((cylinder_length * 0.65) / fin_spacing);
for(f=[1:num_fins]) {
translate([0, 0, f * fin_spacing + 2])
cylinder(d=cylinder_diameter + 2 * fin_overhang, h=fin_thickness, center=true);
}
}
// Head
color(head_color) {
translate([0, 0, cylinder_length * 0.7]) {
// Lower head block
cylinder(d=cylinder_diameter + 2, h=cylinder_length * 0.2);
// Domed top
translate([0, 0, cylinder_length * 0.2])
scale([1, 1, 0.7]) sphere(d=cylinder_diameter + 2);
// Rocker boxes
translate([0, cylinder_diameter * 0.25, cylinder_length * 0.2])
rotate([90, 0, 0])
cylinder(d=cylinder_diameter * 0.5, h=cylinder_diameter * 0.5, center=true);
translate([0, -cylinder_diameter * 0.25, cylinder_length * 0.2])
rotate([90, 0, 0])
cylinder(d=cylinder_diameter * 0.5, h=cylinder_diameter * 0.5, center=true);
// Head fins
head_fins = 3;
for(f=[1:head_fins]) {
translate([0, 0, f * fin_spacing - 1])
cylinder(d=cylinder_diameter + 4, h=fin_thickness, center=true);
}
}
}
}
module draw_rod(p1, p2, dia, rod_color) {
color(rod_color) {
dist = norm(p2 - p1);
dir = (p2 - p1) / dist;
axis = cross([0,0,1], dir);
angle = acos(max(-1, min(1, dir[2])));
translate(p1)
if (norm(axis) > 0.001) {
rotate(a=angle, v=axis)
cylinder(d=dia, h=dist);
} else {
cylinder(d=dia, h=dist);
}
}
}
engine();
Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

@@ -0,0 +1,210 @@
# 11 — Turbofan jet engine
> **Prompt**
>
> A complete high-bypass turbofan: a front fan you can see into, a bypass cowl, an internal core with compressor/turbine stages, outlet guide vanes, and an exhaust plug.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="11-turbofan-jet-engine.gif" alt="Turbofan jet engine — orbiting render" width="380"></p>
**Parametric controls:** 2 dimensions · 10 colours
**What it demonstrates**
- A full high-bypass turbofan — fan, spinner, bypass cowl, core stages, guide vanes, exhaust plug
- ~26 swept fan blades visible through the intake, like the real thing
- Ten distinct part colours across the whole assembly
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `blade_count` | `26` | `[16:2:36]` | Number of front fan blades |
<details>
<summary>OpenSCAD source — <code>11-turbofan-jet-engine.scad</code></summary>
```scad
// [Engine Configuration]
// Show 1/4 cutaway to reveal internal components
cutaway_view = true;
// Number of front fan blades
blade_count = 26; // [16:2:36]
// [Colors]
nacelle_color = "White";
fan_color = "Silver";
spinner_color = "DimGray";
core_cowl_color = "DarkSlateGray";
strut_color = "LightSlateGray";
compressor_color = "LightGray";
combustor_color = "FireBrick";
turbine_color = "Peru";
exhaust_color = "SaddleBrown";
shaft_color = "LightGray";
// Main Execution
// Orient engine horizontally and center it
rotate([-90, 0, 0])
translate([0, 0, -155]) {
engine_casings();
internals();
}
// === Assemblies ===
module engine_casings() {
if (cutaway_view) {
difference() {
casings();
// 90-degree wedge cutout to reveal interior
translate([0, 0, -50])
cube([200, 200, 500]);
}
} else {
casings();
}
}
module casings() {
// Outer Nacelle Cowl
color(nacelle_color)
rotate_extrude($fn=128)
nacelle_profile();
// Inner Core Cowl
color(core_cowl_color)
rotate_extrude($fn=128)
core_cowl_profile();
// Bypass Struts / Outlet Guide Vanes
color(strut_color)
translate([0, 0, 70])
for(i = [0 : 24]) {
rotate([0, 0, i * 360 / 25])
translate([36, 0, 0])
rotate([0, 90, 0])
linear_extrude(height=42, twist=5)
rotate([0, 0, 15])
scale([1, 0.15])
circle(r=8, $fn=16);
}
}
module internals() {
// Nose Spinner
color(spinner_color)
nose_spinner();
// Front Fan Blades
color(fan_color)
translate([0, 0, 40])
for(i = [0 : blade_count - 1]) {
rotate([0, 0, i * 360 / blade_count])
translate([23, 0, 0])
rotate([-15, 0, 0])
fan_blade();
}
// Central Shaft
color(shaft_color)
rotate_extrude($fn=64)
polygon([
[0, 40], [24, 40],
[16, 55],
[16, 135],
[16, 240],
[0, 240]
]);
// Compressor Stages
color(compressor_color) {
blade_row(65, 36, 15, 33, 10);
blade_row(80, 36, 15, 31, 9);
blade_row(95, 40, 15, 29, 8);
blade_row(110, 40, 15, 27, 7);
blade_row(125, 46, 15, 25, 6);
}
// Combustor
color(combustor_color)
translate([0, 0, 135])
rotate_extrude($fn=64)
polygon([
[16, 0], [21, 5], [21, 10],
[18, 15],
[21, 20], [21, 25], [16, 30]
]);
// Turbine Stages
color(turbine_color) {
blade_row(180, 30, 15, 28, 12);
blade_row(195, 30, 15, 27.5, 12);
blade_row(210, 30, 15, 26.5, 11);
blade_row(225, 30, 15, 25.5, 10);
}
// Exhaust Plug Cone
color(exhaust_color)
translate([0, 0, 240])
cylinder(r1=16, r2=2, h=70, $fn=64);
}
// === Components ===
module nose_spinner() {
rotate_extrude($fn=64)
intersection() {
square([30, 40]);
translate([-20, 40])
circle(r=45, $fn=128);
}
}
module fan_blade() {
rotate([0, 90, 0])
linear_extrude(height=50, twist=-50, slices=30, scale=0.7)
scale([1, 0.12])
circle(r=20, $fn=32);
}
module blade_row(z_pos, count, inner_r, outer_r, chord) {
translate([0, 0, z_pos])
for(i = [0 : count - 1]) {
rotate([0, 0, i * 360 / count])
translate([inner_r - 1, 0, 0])
rotate([25, 90, 0]) // Pitch angle
linear_extrude(height = outer_r - inner_r + 2)
scale([1, 0.2])
circle(r=chord/2, $fn=16);
}
}
module nacelle_profile() {
offset(r=1.5, $fn=16)
polygon([
[76, 15], // front intake lip
[82, 50], // thickest outer curve
[78, 150], // rear outer trailing edge
[76, 150], // rear inner
[77, 80], // inner bypass wall
[75.5, 40], // fan clearance
[74.5, 20] // front inner transition
]);
}
module core_cowl_profile() {
offset(r=1, $fn=16)
polygon([
[36, 55], // front outer (behind fan)
[38, 90], // max thickness
[26, 260], // rear outer tapering nozzle
[24, 260], // rear inner nozzle lip
[36, 90], // inner max
[34, 55] // front inner (compressor intake)
]);
}
```
</details>
@@ -0,0 +1,179 @@
// [Engine Configuration]
// Show 1/4 cutaway to reveal internal components
cutaway_view = true;
// Number of front fan blades
blade_count = 26; // [16:2:36]
// [Colors]
nacelle_color = "White";
fan_color = "Silver";
spinner_color = "DimGray";
core_cowl_color = "DarkSlateGray";
strut_color = "LightSlateGray";
compressor_color = "LightGray";
combustor_color = "FireBrick";
turbine_color = "Peru";
exhaust_color = "SaddleBrown";
shaft_color = "LightGray";
// Main Execution
// Orient engine horizontally and center it
rotate([-90, 0, 0])
translate([0, 0, -155]) {
engine_casings();
internals();
}
// === Assemblies ===
module engine_casings() {
if (cutaway_view) {
difference() {
casings();
// 90-degree wedge cutout to reveal interior
translate([0, 0, -50])
cube([200, 200, 500]);
}
} else {
casings();
}
}
module casings() {
// Outer Nacelle Cowl
color(nacelle_color)
rotate_extrude($fn=128)
nacelle_profile();
// Inner Core Cowl
color(core_cowl_color)
rotate_extrude($fn=128)
core_cowl_profile();
// Bypass Struts / Outlet Guide Vanes
color(strut_color)
translate([0, 0, 70])
for(i = [0 : 24]) {
rotate([0, 0, i * 360 / 25])
translate([36, 0, 0])
rotate([0, 90, 0])
linear_extrude(height=42, twist=5)
rotate([0, 0, 15])
scale([1, 0.15])
circle(r=8, $fn=16);
}
}
module internals() {
// Nose Spinner
color(spinner_color)
nose_spinner();
// Front Fan Blades
color(fan_color)
translate([0, 0, 40])
for(i = [0 : blade_count - 1]) {
rotate([0, 0, i * 360 / blade_count])
translate([23, 0, 0])
rotate([-15, 0, 0])
fan_blade();
}
// Central Shaft
color(shaft_color)
rotate_extrude($fn=64)
polygon([
[0, 40], [24, 40],
[16, 55],
[16, 135],
[16, 240],
[0, 240]
]);
// Compressor Stages
color(compressor_color) {
blade_row(65, 36, 15, 33, 10);
blade_row(80, 36, 15, 31, 9);
blade_row(95, 40, 15, 29, 8);
blade_row(110, 40, 15, 27, 7);
blade_row(125, 46, 15, 25, 6);
}
// Combustor
color(combustor_color)
translate([0, 0, 135])
rotate_extrude($fn=64)
polygon([
[16, 0], [21, 5], [21, 10],
[18, 15],
[21, 20], [21, 25], [16, 30]
]);
// Turbine Stages
color(turbine_color) {
blade_row(180, 30, 15, 28, 12);
blade_row(195, 30, 15, 27.5, 12);
blade_row(210, 30, 15, 26.5, 11);
blade_row(225, 30, 15, 25.5, 10);
}
// Exhaust Plug Cone
color(exhaust_color)
translate([0, 0, 240])
cylinder(r1=16, r2=2, h=70, $fn=64);
}
// === Components ===
module nose_spinner() {
rotate_extrude($fn=64)
intersection() {
square([30, 40]);
translate([-20, 40])
circle(r=45, $fn=128);
}
}
module fan_blade() {
rotate([0, 90, 0])
linear_extrude(height=50, twist=-50, slices=30, scale=0.7)
scale([1, 0.12])
circle(r=20, $fn=32);
}
module blade_row(z_pos, count, inner_r, outer_r, chord) {
translate([0, 0, z_pos])
for(i = [0 : count - 1]) {
rotate([0, 0, i * 360 / count])
translate([inner_r - 1, 0, 0])
rotate([25, 90, 0]) // Pitch angle
linear_extrude(height = outer_r - inner_r + 2)
scale([1, 0.2])
circle(r=chord/2, $fn=16);
}
}
module nacelle_profile() {
offset(r=1.5, $fn=16)
polygon([
[76, 15], // front intake lip
[82, 50], // thickest outer curve
[78, 150], // rear outer trailing edge
[76, 150], // rear inner
[77, 80], // inner bypass wall
[75.5, 40], // fan clearance
[74.5, 20] // front inner transition
]);
}
module core_cowl_profile() {
offset(r=1, $fn=16)
polygon([
[36, 55], // front outer (behind fan)
[38, 90], // max thickness
[26, 260], // rear outer tapering nozzle
[24, 260], // rear inner nozzle lip
[36, 90], // inner max
[34, 55] // front inner (compressor intake)
]);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

@@ -0,0 +1,180 @@
# 12 — Axial turbine blisk
> **Prompt**
>
> Model an axial-flow turbine blisk (bladed disk) like a jet engine compressor stage: a central hub with a shaft bore and a single ring of about 28 thin aerofoil blades around the rim, each blade clearly twisted along its height from root to tip like a real turbine blade.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="12-axial-turbine-blisk.gif" alt="Axial turbine blisk — orbiting render" width="380"></p>
**Parametric controls:** 14 dimensions · 1 colour
**What it demonstrates**
- A single-stage bladed disk: a ring of twisted aerofoil blades on a central hub
- Each blade twists root-to-tip like a real turbine blade
- Keyed central shaft bore
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `num_blades` | `28` | `[10:1:60]` | Number of blades around the hub |
| `hub_radius` | `45` | `[20:1:100]` | Outer radius of the central hub |
| `bore_radius` | `12` | `[5:1:30]` | Radius of the central shaft hole |
| `blade_height` | `45` | `[20:1:100]` | Radial height of each blade |
| `hub_center_thickness` | `30` | `[10:1:60]` | Thickness of the hub at the center |
| `hub_rim_thickness` | `22` | `[10:1:50]` | Thickness of the hub at the rim |
| `flange_width` | `8` | `[0:1:20]` | Width of the collar around the bore |
| `flange_height` | `5` | `[0:1:20]` | Height of the collar around the bore |
| `blade_chord` | `22` | `[10:1:50]` | Length of the blade profile (chord) |
| `root_stagger` | `30` | `[0:1:90]` | Angle of the blade at the root |
| `blade_twist` | `35` | `[-60:1:60]` | Total twist from root to tip |
| `camber_pct` | `10` | `[0:1:20]` | Aerodynamic camber (curvature) |
| `thickness_pct` | `6` | `[2:1:15]` | Maximum thickness of the blade profile |
| `steps` | `30` | `Resolution per side` | |
<details>
<summary>OpenSCAD source — <code>12-axial-turbine-blisk.scad</code></summary>
```scad
// Axial-Flow Turbine Blisk
// A parametric model of a jet engine compressor stage
/* [Blisk Dimensions] */
// Number of blades around the hub
num_blades = 28; // [10:1:60]
// Outer radius of the central hub
hub_radius = 45; // [20:1:100]
// Radius of the central shaft hole
bore_radius = 12; // [5:1:30]
// Radial height of each blade
blade_height = 45; // [20:1:100]
/* [Hub Profile] */
// Thickness of the hub at the center
hub_center_thickness = 30; // [10:1:60]
// Thickness of the hub at the rim
hub_rim_thickness = 22; // [10:1:50]
// Width of the collar around the bore
flange_width = 8; // [0:1:20]
// Height of the collar around the bore
flange_height = 5; // [0:1:20]
/* [Blade Aerodynamics] */
// Length of the blade profile (chord)
blade_chord = 22; // [10:1:50]
// Angle of the blade at the root
root_stagger = 30; // [0:1:90]
// Total twist from root to tip
blade_twist = 35; // [-60:1:60]
// Aerodynamic camber (curvature)
camber_pct = 10; // [0:1:20]
// Maximum thickness of the blade profile
thickness_pct = 6; // [2:1:15]
/* [Settings] */
blisk_color = "#A8AEB3";
$fn = 72;
// --- Main Model ---
color(blisk_color)
difference() {
union() {
hub();
blade_array();
}
// Central bore keyway to lock the blisk to a shaft
translate([bore_radius + 1.5, 0, 0])
cube([4, 6, hub_center_thickness * 3], center=true);
}
// --- Modules ---
module hub() {
// Generates the profiled central disk
rotate_extrude(convexity = 4)
polygon([
[bore_radius, hub_center_thickness/2 + flange_height],
[bore_radius + flange_width, hub_center_thickness/2 + flange_height],
[bore_radius + flange_width, hub_center_thickness/2],
[hub_radius, hub_rim_thickness/2],
[hub_radius, -hub_rim_thickness/2],
[bore_radius + flange_width, -hub_center_thickness/2],
[bore_radius + flange_width, -hub_center_thickness/2 - flange_height],
[bore_radius, -hub_center_thickness/2 - flange_height]
]);
}
module blade_array() {
// Radial array of aerofoil blades
for (i = [0 : num_blades - 1]) {
rotate([0, 0, i * 360 / num_blades])
// Embed the blade root slightly into the hub rim
translate([hub_radius - 2, 0, 0])
// Lay the blade down so it points outward radially
rotate([0, 90, 0])
// Extrude with aerodynamic twist
linear_extrude(height = blade_height + 2, twist = blade_twist, slices = 45, convexity = 2)
// Set the root angle (stagger)
rotate([0, 0, root_stagger])
// Center the aerofoil on the extrusion axis
translate([-blade_chord/2, 0])
naca_airfoil(blade_chord, thickness_pct, camber_pct, 40);
}
}
module naca_airfoil(c, t_pct, m_pct, p_pct) {
// Generates a robust NACA-style highly cambered profile
t_val = t_pct / 100;
m = m_pct / 100;
p = p_pct / 100;
// Thickness distribution function
function y_t(x) = 5 * t_val * c * (0.2969*sqrt(abs(x/c)) - 0.1260*(x/c) - 0.3516*pow(x/c,2) + 0.2843*pow(x/c,3) - 0.1015*pow(x/c,4));
// Camber line function
function y_c(x) = (m == 0) ? 0 :
( (x/c) <= p ) ? m * c / pow(p,2) * (2*p*(x/c) - pow(x/c,2))
: m * c / pow(1-p,2) * ((1-2*p) + 2*p*(x/c) - pow(x/c,2));
// Camber gradient for normal vector
function dy_c(x) = (m == 0) ? 0 :
( (x/c) <= p ) ? 2 * m / pow(p,2) * (p - (x/c))
: 2 * m / pow(1-p,2) * (p - (x/c));
function theta(x) = atan(dy_c(x));
steps = 30; // Resolution per side
// Generate upper surface points
pts_upper = [ for (i=[0:steps])
let (
x = c * (1 - cos(i * 180 / steps))/2,
yt = y_t(x),
yc = y_c(x),
th = theta(x)
)
[ x - yt * sin(th), yc + yt * cos(th) ]
];
// Generate lower surface points
pts_lower = [ for (i=[steps:-1:0])
let (
x = c * (1 - cos(i * 180 / steps))/2,
yt = y_t(x),
yc = y_c(x),
th = theta(x)
)
[ x + yt * sin(th), yc - yt * cos(th) ]
];
// Combine and apply offset to guarantee manifold trailing edges and robust printability
offset(r=0.6)
polygon(concat(pts_upper, pts_lower));
}
```
</details>
@@ -0,0 +1,136 @@
// Axial-Flow Turbine Blisk
// A parametric model of a jet engine compressor stage
/* [Blisk Dimensions] */
// Number of blades around the hub
num_blades = 28; // [10:1:60]
// Outer radius of the central hub
hub_radius = 45; // [20:1:100]
// Radius of the central shaft hole
bore_radius = 12; // [5:1:30]
// Radial height of each blade
blade_height = 45; // [20:1:100]
/* [Hub Profile] */
// Thickness of the hub at the center
hub_center_thickness = 30; // [10:1:60]
// Thickness of the hub at the rim
hub_rim_thickness = 22; // [10:1:50]
// Width of the collar around the bore
flange_width = 8; // [0:1:20]
// Height of the collar around the bore
flange_height = 5; // [0:1:20]
/* [Blade Aerodynamics] */
// Length of the blade profile (chord)
blade_chord = 22; // [10:1:50]
// Angle of the blade at the root
root_stagger = 30; // [0:1:90]
// Total twist from root to tip
blade_twist = 35; // [-60:1:60]
// Aerodynamic camber (curvature)
camber_pct = 10; // [0:1:20]
// Maximum thickness of the blade profile
thickness_pct = 6; // [2:1:15]
/* [Settings] */
blisk_color = "#A8AEB3";
$fn = 72;
// --- Main Model ---
color(blisk_color)
difference() {
union() {
hub();
blade_array();
}
// Central bore keyway to lock the blisk to a shaft
translate([bore_radius + 1.5, 0, 0])
cube([4, 6, hub_center_thickness * 3], center=true);
}
// --- Modules ---
module hub() {
// Generates the profiled central disk
rotate_extrude(convexity = 4)
polygon([
[bore_radius, hub_center_thickness/2 + flange_height],
[bore_radius + flange_width, hub_center_thickness/2 + flange_height],
[bore_radius + flange_width, hub_center_thickness/2],
[hub_radius, hub_rim_thickness/2],
[hub_radius, -hub_rim_thickness/2],
[bore_radius + flange_width, -hub_center_thickness/2],
[bore_radius + flange_width, -hub_center_thickness/2 - flange_height],
[bore_radius, -hub_center_thickness/2 - flange_height]
]);
}
module blade_array() {
// Radial array of aerofoil blades
for (i = [0 : num_blades - 1]) {
rotate([0, 0, i * 360 / num_blades])
// Embed the blade root slightly into the hub rim
translate([hub_radius - 2, 0, 0])
// Lay the blade down so it points outward radially
rotate([0, 90, 0])
// Extrude with aerodynamic twist
linear_extrude(height = blade_height + 2, twist = blade_twist, slices = 45, convexity = 2)
// Set the root angle (stagger)
rotate([0, 0, root_stagger])
// Center the aerofoil on the extrusion axis
translate([-blade_chord/2, 0])
naca_airfoil(blade_chord, thickness_pct, camber_pct, 40);
}
}
module naca_airfoil(c, t_pct, m_pct, p_pct) {
// Generates a robust NACA-style highly cambered profile
t_val = t_pct / 100;
m = m_pct / 100;
p = p_pct / 100;
// Thickness distribution function
function y_t(x) = 5 * t_val * c * (0.2969*sqrt(abs(x/c)) - 0.1260*(x/c) - 0.3516*pow(x/c,2) + 0.2843*pow(x/c,3) - 0.1015*pow(x/c,4));
// Camber line function
function y_c(x) = (m == 0) ? 0 :
( (x/c) <= p ) ? m * c / pow(p,2) * (2*p*(x/c) - pow(x/c,2))
: m * c / pow(1-p,2) * ((1-2*p) + 2*p*(x/c) - pow(x/c,2));
// Camber gradient for normal vector
function dy_c(x) = (m == 0) ? 0 :
( (x/c) <= p ) ? 2 * m / pow(p,2) * (p - (x/c))
: 2 * m / pow(1-p,2) * (p - (x/c));
function theta(x) = atan(dy_c(x));
steps = 30; // Resolution per side
// Generate upper surface points
pts_upper = [ for (i=[0:steps])
let (
x = c * (1 - cos(i * 180 / steps))/2,
yt = y_t(x),
yc = y_c(x),
th = theta(x)
)
[ x - yt * sin(th), yc + yt * cos(th) ]
];
// Generate lower surface points
pts_lower = [ for (i=[steps:-1:0])
let (
x = c * (1 - cos(i * 180 / steps))/2,
yt = y_t(x),
yc = y_c(x),
th = theta(x)
)
[ x + yt * sin(th), yc - yt * cos(th) ]
];
// Combine and apply offset to guarantee manifold trailing edges and robust printability
offset(r=0.6)
polygon(concat(pts_upper, pts_lower));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

+490
View File
@@ -0,0 +1,490 @@
# 13 — V8 engine
> **Prompt**
>
> A complete V8 internal combustion engine: two banks of four cylinders in a 90° V, cylinder heads with ribbed valve covers, an intake manifold in the valley, exhaust headers down each bank, a crankshaft with counterweights, pistons and connecting rods, a front pulley, and an oil pan.
Fully parametric OpenSCAD built from the prompt above — adjustable dimensions and colours, exported as `.SCAD` and rendered to the orbiting preview with [`render.sh`](render.sh).
<p align="center"><img src="13-v8-engine.gif" alt="V8 engine — orbiting render" width="380"></p>
**Parametric controls:** 22 dimensions · 8 colours
**What it demonstrates**
- A complete V8: 90° cylinder banks, heads, ribbed valve covers, intake manifold, exhaust headers, and oil pan
- Even the internal rotating assembly — crankshaft with counterweights, pistons, and connecting rods — modelled inside the block
- 22 parametric controls across 8 part colours; ~460 lines of OpenSCAD — the most complex benchmark here
**Editable parameters**
| Parameter | Default | Range / options | Description |
| --- | --- | --- | --- |
| `crank_angle` | `45` | `[0:1:360]` | Rotate the crankshaft to see the pistons move |
<details>
<summary>OpenSCAD source — <code>13-v8-engine.scad</code></summary>
```scad
// V8 Engine Model
/* [Visibility] */
// Enable cutaway view to see internal mechanics
cutaway = true;
/* [Animation] */
// Rotate the crankshaft to see the pistons move
crank_angle = 45; // [0:1:360]
/* [Colors] */
block_color = "Silver";
head_color = "DarkGray";
valve_cover_color = "FireBrick";
intake_color = "DimGray";
exhaust_color = "Peru";
pulley_color = "Black";
oil_pan_color = "DarkSlateGray";
internals_color = "LightSteelBlue";
/* [Hidden] */
$fn = 32;
bore = 10;
stroke = 15;
crank_r = stroke / 2;
conrod_len = 35;
cyl_spacing = 28;
deck_height = 50;
bank_angle = 45;
pin_angles = [0, 90, 270, 180];
main_y = [0, 28, 56, 84, 112];
pin_y_start = [8, 36, 64, 92];
// Core kinematics calculation for piston position
function get_D(crank_ang, bank_ang) =
let(Px = crank_r * sin(crank_ang),
Pz = crank_r * cos(crank_ang),
K = Px * sin(bank_ang) + Pz * cos(bank_ang))
K + sqrt(K*K - crank_r*crank_r + conrod_len*conrod_len);
module assemble_engine() {
engine_stand();
engine_block();
heads();
valve_covers();
spark_plugs();
oil_pan();
intake_manifold();
exhaust_headers();
front_accessories();
alternator();
belt();
front_pulley();
fan();
flywheel();
internals(crank_angle);
}
module cutaway_cutter() {
if(cutaway) {
intersection() {
// Diagonal cut plane along cylinder axis
translate([0, 50, 0])
rotate([0, bank_angle, 0])
translate([0.1, 0, -50])
cube([80, 100, 150]);
// Limit strictly to right half to preserve left side
translate([0, 50, -50]) cube([100, 100, 150]);
}
}
}
module engine_block() {
color(block_color)
difference() {
union() {
// Main block body
translate([-25, 0, -10]) cube([50, 120, 20]);
// Banks
intersect_bank(bank_angle);
intersect_bank(-bank_angle);
// Side reinforcement ribs
for(y = main_y) {
translate([-26, y-2, -10]) cube([52, 4, 15]);
}
// Rear bell housing flange
translate([-26, -4, -10]) cube([52, 4, 30]);
}
// Hollow out valley
translate([-10, -1, 20]) cube([20, 122, 40]);
// Hollow out crankcase
translate([0, -1, 0]) rotate([-90,0,0]) cylinder(r=18, h=122);
translate([-18, -1, -20]) cube([36, 122, 20]);
// Cylinder bores
for(i=[0:3]) {
y_s = pin_y_start[i];
// Right bank
translate([0, y_s + 7.5, 0])
rotate([0, bank_angle, 0])
translate([0, 0, 10]) cylinder(r=bore, h=deck_height+10);
// Left bank
translate([0, y_s + 12.5, 0])
rotate([0, -bank_angle, 0])
translate([0, 0, 10]) cylinder(r=bore, h=deck_height+10);
}
cutaway_cutter();
}
}
module intersect_bank(ang) {
rotate([0, ang, 0])
translate([-14, 0, -20]) cube([28, 120, deck_height + 20]);
}
module heads() {
color(head_color)
difference() {
union() {
head_shape(bank_angle);
head_shape(-bank_angle);
}
cutaway_cutter();
}
}
module head_shape(ang) {
rotate([0, ang, 0])
translate([-14, 0, deck_height])
difference() {
cube([28, 120, 18]);
// Spark plug recesses
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
translate([14, y_s, 9]) rotate([0, 90, 0]) cylinder(r=4, h=10, center=true);
}
}
}
module spark_plugs() {
color("White")
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
// Right
rotate([0, bank_angle, 0]) translate([14, y_s, deck_height + 9]) rotate([0, 90, 0]) cylinder(r=2, h=8);
// Left
rotate([0, -bank_angle, 0]) translate([-14, y_s, deck_height + 9]) rotate([0, -90, 0]) cylinder(r=2, h=8);
}
}
module valve_covers() {
color(valve_cover_color)
difference() {
union() {
valve_cover_shape(bank_angle);
valve_cover_shape(-bank_angle);
}
cutaway_cutter();
}
}
module valve_cover_shape(ang) {
rotate([0, ang, 0])
translate([-12, 0, deck_height + 18]) {
// Base
hull() {
cube([24, 120, 2]);
translate([2, 2, 8]) cube([20, 116, 2]);
}
// Ribs
for(y=[5 : 10 : 115]) {
translate([4, y, 10]) cube([16, 4, 2]);
}
// Oil cap
if(ang > 0) {
translate([12, 20, 10]) cylinder(r=5, h=4);
}
}
}
module oil_pan() {
color(oil_pan_color)
difference() {
union() {
hull() {
translate([-25, 0, -10]) cube([50, 120, 1]);
translate([-15, 10, -30]) cube([30, 100, 1]);
}
for(y=[15:5:105]) {
translate([-15, y, -32]) cube([30, 2, 4]);
}
}
// Hollow inside
hull() {
translate([-23, 2, -10]) cube([46, 116, 1]);
translate([-13, 12, -28]) cube([26, 96, 1]);
}
cutaway_cutter();
}
}
module intake_manifold() {
color(intake_color)
difference() {
union() {
// Central plenum
translate([-10, 10, 35]) cube([20, 100, 15]);
// Runners to Right head
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
hull() {
translate([10, y_s-4, 40]) cube([2, 8, 8]);
rotate([0, bank_angle, 0]) translate([-14, y_s-4, deck_height-2]) cube([5, 8, 12]);
}
}
// Runners to Left head
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
hull() {
translate([-12, y_s-4, 40]) cube([2, 8, 8]);
rotate([0, -bank_angle, 0]) translate([9, y_s-4, deck_height-2]) cube([5, 8, 12]);
}
}
// Throttle body
translate([0, 110, 42.5]) rotate([-90,0,0]) cylinder(r=8, h=15);
}
cutaway_cutter();
}
}
module exhaust_headers() {
color(exhaust_color)
difference() {
union() {
// Right bank
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
header_pipe(bank_angle, 1, y_s);
}
// Right Collector
translate([50, 10, -25]) rotate([-90,0,0]) cylinder(r=6, h=100);
// Left bank
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
header_pipe(-bank_angle, -1, y_s);
}
// Left Collector
translate([-50, 10, -25]) rotate([-90,0,0]) cylinder(r=6, h=100);
}
cutaway_cutter();
}
}
module header_pipe(ang, dir, y) {
gx = dir * (14*cos(45) + (deck_height+7.5)*sin(45));
gz = (deck_height+7.5)*cos(45) - 14*sin(45);
hull() {
translate([gx, y, gz]) rotate([0, 90, 0]) cylinder(r=4, h=2, center=true);
translate([gx + dir*5, y, gz - 5]) sphere(r=4);
}
hull() {
translate([gx + dir*5, y, gz - 5]) sphere(r=4);
translate([dir*50, y, -25]) sphere(r=5);
}
}
module front_accessories() {
color(block_color)
difference() {
union() {
// Timing cover
hull() {
translate([-25, 120, -10]) cube([50, 4, 20]);
translate([-15, 120, 40]) cube([30, 4, 10]);
}
// Water pump
translate([0, 124, 30]) rotate([-90,0,0]) cylinder(r=12, h=8);
// Water pump pulley
color(pulley_color) translate([0, 132, 30]) rotate([-90,0,0]) {
cylinder(r=10, h=8);
translate([0,0,8]) cylinder(r=8, h=2);
}
}
cutaway_cutter();
}
}
module alternator() {
color("LightGray")
difference() {
union() {
translate([20, 125, 40]) rotate([-90,0,0]) cylinder(r=8, h=10);
color(pulley_color) translate([20, 135, 40]) rotate([-90,0,0]) cylinder(r=6, h=5);
}
cutaway_cutter();
}
}
module belt() {
color("#222222")
difference() {
hull() {
translate([0, 134, 30]) rotate([-90,0,0]) cylinder(r=10, h=4);
translate([0, 134, 0]) rotate([-90,0,0]) cylinder(r=16, h=4);
translate([20, 135, 40]) rotate([-90,0,0]) cylinder(r=6, h=4);
}
hull() {
translate([0, 133, 30]) rotate([-90,0,0]) cylinder(r=8, h=6);
translate([0, 133, 0]) rotate([-90,0,0]) cylinder(r=14, h=6);
translate([20, 134, 40]) rotate([-90,0,0]) cylinder(r=4, h=6);
}
cutaway_cutter();
}
}
module fan() {
color("Silver")
difference() {
union() {
translate([0, 142, 30]) rotate([-90,0,0]) cylinder(r=5, h=2);
for(a=[0:72:359]) {
rotate([0, a, 0])
translate([5, 142.5, 30])
rotate([20, 0, 0])
cube([25, 2, 8]);
}
}
cutaway_cutter();
}
}
module front_pulley() {
color(pulley_color)
difference() {
translate([0, 124, 0]) rotate([-90,0,0]) {
cylinder(r=12, h=8);
translate([0,0,8]) cylinder(r=16, h=8);
}
cutaway_cutter();
}
}
module flywheel() {
color(pulley_color)
translate([0, -5, 0]) rotate([-90,0,0]) cylinder(r=25, h=5);
}
module engine_stand() {
color("Orange")
union() {
// Floor base
translate([-40, -40, -60]) cube([80, 160, 5]);
// Upright
translate([-15, -30, -55]) cube([30, 15, 75]);
// Stand head
translate([-10, -15, -10]) cube([20, 10, 20]);
// Standoff mounts to bell housing
for(x=[-15, 15]) {
for(z=[-5, 15]) {
translate([x, -15, z]) rotate([-90,0,0]) cylinder(r=2, h=15);
}
}
}
}
module internals(crank_angle_offset=0) {
color(internals_color) {
// Main journals
for(y = main_y) {
translate([0, y, 0]) {
translate([0, 4, 0]) rotate([-90,0,0]) cylinder(r=5, h=8, center=true);
}
}
for(i = [0:3]) {
ang = crank_angle_offset + pin_angles[i];
y_s = pin_y_start[i];
// Web 1
web(ang, y_s);
// Crank Pin
translate([0, y_s + 5, 0])
rotate([0, ang, 0])
translate([0, 0, crank_r])
translate([0, 5, 0]) rotate([-90,0,0]) cylinder(r=4, h=10, center=true);
// Web 2
web(ang, y_s + 15);
// Piston 1 (Right Bank)
piston_and_rod(ang, bank_angle, y_s + 7.5);
// Piston 2 (Left Bank)
piston_and_rod(ang, -bank_angle, y_s + 12.5);
}
}
}
module web(angle, y_pos) {
translate([0, y_pos, 0])
rotate([0, angle, 0]) {
hull() {
translate([0, 2.5, 0]) rotate([-90,0,0]) cylinder(r=6, h=5, center=true);
translate([0, 2.5, crank_r]) rotate([-90,0,0]) cylinder(r=6, h=5, center=true);
}
// Counterweight
translate([0, 2.5, -crank_r/2 + 1])
cube([16, 5, crank_r + 10], center=true);
}
}
module piston_and_rod(crank_ang, bank_ang, y_pos) {
D = get_D(crank_ang, bank_ang);
Px = crank_r * sin(crank_ang);
Pz = crank_r * cos(crank_ang);
Wx = D * sin(bank_ang);
Wz = D * cos(bank_ang);
rod_ang = atan2(Wx - Px, Wz - Pz);
translate([0, y_pos, 0]) {
// Conrod
translate([Px, 0, Pz])
rotate([0, rod_ang, 0])
translate([0, -2.5, 0]) {
hull() {
rotate([-90,0,0]) cylinder(r=4, h=5);
translate([0, 0, conrod_len]) rotate([-90,0,0]) cylinder(r=3, h=5);
}
}
// Piston pin
translate([Wx, 0, Wz])
rotate([0, bank_ang, 0])
rotate([-90,0,0]) cylinder(r=2.5, h=10, center=true);
// Piston body
translate([Wx, 0, Wz])
rotate([0, bank_ang, 0]) {
translate([0, 0, -6]) rotate([-90,0,0]) cylinder(r=bore-0.5, h=12, center=true);
translate([0, 0, 6]) rotate([-90,0,0]) cylinder(r=bore-0.5, h=4, center=true);
}
}
}
assemble_engine();
```
</details>
+472
View File
@@ -0,0 +1,472 @@
// V8 Engine Model
/* [Visibility] */
// Enable cutaway view to see internal mechanics
cutaway = true;
/* [Animation] */
// Rotate the crankshaft to see the pistons move
crank_angle = 45; // [0:1:360]
/* [Colors] */
block_color = "Silver";
head_color = "DarkGray";
valve_cover_color = "FireBrick";
intake_color = "DimGray";
exhaust_color = "Peru";
pulley_color = "Black";
oil_pan_color = "DarkSlateGray";
internals_color = "LightSteelBlue";
/* [Hidden] */
$fn = 32;
bore = 10;
stroke = 15;
crank_r = stroke / 2;
conrod_len = 35;
cyl_spacing = 28;
deck_height = 50;
bank_angle = 45;
pin_angles = [0, 90, 270, 180];
main_y = [0, 28, 56, 84, 112];
pin_y_start = [8, 36, 64, 92];
// Core kinematics calculation for piston position
function get_D(crank_ang, bank_ang) =
let(Px = crank_r * sin(crank_ang),
Pz = crank_r * cos(crank_ang),
K = Px * sin(bank_ang) + Pz * cos(bank_ang))
K + sqrt(K*K - crank_r*crank_r + conrod_len*conrod_len);
module assemble_engine() {
engine_stand();
engine_block();
heads();
valve_covers();
spark_plugs();
oil_pan();
intake_manifold();
exhaust_headers();
front_accessories();
alternator();
belt();
front_pulley();
fan();
flywheel();
internals(crank_angle);
}
module cutaway_cutter() {
if(cutaway) {
intersection() {
// Diagonal cut plane along right cylinder axis
rotate([0, bank_angle, 0])
// Massive bounding box starting at main journal 3 to cleanly slice front-right
translate([0.1, 56, -150])
cube([300, 200, 400]);
// Limit strictly to right side of the engine to preserve left bank
translate([0.1, 56, -150]) cube([300, 200, 400]);
}
}
}
module engine_block() {
color(block_color)
difference() {
union() {
// Main block body
translate([-25, 0, -10]) cube([50, 120, 20]);
// Banks
intersect_bank(bank_angle);
intersect_bank(-bank_angle);
// Side reinforcement ribs
for(y = main_y) {
translate([-26, y-2, -10]) cube([52, 4, 15]);
}
// Rear bell housing flange
translate([-26, -4, -10]) cube([52, 4, 30]);
// Main bearing supports (bulkheads) connecting block to crank
for(y = main_y) {
translate([-15, y, -15]) cube([30, 8, 15]);
}
}
// Hollow out valley
translate([-10, -1, 20]) cube([20, 122, 40]);
// Hollow out crankcase bays between the bulkheads
for(i = [0:3]) {
y_start_hollow = main_y[i] + 8;
hollow_len = main_y[i+1] - y_start_hollow;
if (hollow_len > 0) {
translate([0, y_start_hollow, 0]) rotate([-90,0,0]) cylinder(r=18, h=hollow_len);
translate([-18, y_start_hollow, -20]) cube([36, hollow_len, 20]);
}
}
// Front and rear crank clearance
translate([0, -5, 0]) rotate([-90,0,0]) cylinder(r=18, h=5);
translate([-18, -5, -20]) cube([36, 5, 20]);
translate([0, 120, 0]) rotate([-90,0,0]) cylinder(r=18, h=5);
translate([-18, 120, -20]) cube([36, 5, 20]);
// Cut the shaft hole for the crank through the solid bulkheads perfectly matching radius
translate([0, -10, 0]) rotate([-90,0,0]) cylinder(r=5, h=140);
// Cylinder bores
for(i=[0:3]) {
y_s = pin_y_start[i];
// Right bank
translate([0, y_s + 7.5, 0])
rotate([0, bank_angle, 0])
translate([0, 0, 10]) cylinder(r=bore, h=deck_height+10);
// Left bank
translate([0, y_s + 12.5, 0])
rotate([0, -bank_angle, 0])
translate([0, 0, 10]) cylinder(r=bore, h=deck_height+10);
}
cutaway_cutter();
}
}
module intersect_bank(ang) {
rotate([0, ang, 0])
translate([-14, 0, -20]) cube([28, 120, deck_height + 20]);
}
module heads() {
color(head_color)
difference() {
union() {
head_shape(bank_angle);
head_shape(-bank_angle);
}
cutaway_cutter();
}
}
module head_shape(ang) {
rotate([0, ang, 0])
translate([-14, 0, deck_height])
difference() {
cube([28, 120, 18]);
// Spark plug recesses
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
translate([14, y_s, 9]) rotate([0, 90, 0]) cylinder(r=4, h=10, center=true);
}
}
}
module spark_plugs() {
color("White")
difference() {
union() {
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
// Right
rotate([0, bank_angle, 0]) translate([14, y_s, deck_height + 9]) rotate([0, 90, 0]) cylinder(r=2, h=8);
// Left
rotate([0, -bank_angle, 0]) translate([-14, y_s, deck_height + 9]) rotate([0, -90, 0]) cylinder(r=2, h=8);
}
}
cutaway_cutter();
}
}
module valve_covers() {
color(valve_cover_color)
difference() {
union() {
valve_cover_shape(bank_angle);
valve_cover_shape(-bank_angle);
}
cutaway_cutter();
}
}
module valve_cover_shape(ang) {
rotate([0, ang, 0])
translate([-12, 0, deck_height + 18]) {
// Base
hull() {
cube([24, 120, 2]);
translate([2, 2, 8]) cube([20, 116, 2]);
}
// Ribs
for(y=[5 : 10 : 115]) {
translate([4, y, 10]) cube([16, 4, 2]);
}
// Oil cap
if(ang > 0) {
translate([12, 20, 10]) cylinder(r=5, h=4);
}
}
}
module oil_pan() {
color(oil_pan_color)
difference() {
union() {
hull() {
translate([-25, 0, -10]) cube([50, 120, 1]);
translate([-15, 10, -30]) cube([30, 100, 1]);
}
for(y=[15:5:105]) {
translate([-15, y, -32]) cube([30, 2, 4]);
}
}
// Hollow inside
hull() {
translate([-23, 2, -10]) cube([46, 116, 1]);
translate([-13, 12, -28]) cube([26, 96, 1]);
}
cutaway_cutter();
}
}
module intake_manifold() {
color(intake_color)
difference() {
union() {
// Central plenum
translate([-10, 10, 35]) cube([20, 100, 15]);
// Runners to Right head
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
hull() {
translate([10, y_s-4, 40]) cube([2, 8, 8]);
rotate([0, bank_angle, 0]) translate([-14, y_s-4, deck_height-2]) cube([5, 8, 12]);
}
}
// Runners to Left head
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
hull() {
translate([-12, y_s-4, 40]) cube([2, 8, 8]);
rotate([0, -bank_angle, 0]) translate([9, y_s-4, deck_height-2]) cube([5, 8, 12]);
}
}
// Throttle body
translate([0, 110, 42.5]) rotate([-90,0,0]) cylinder(r=8, h=15);
}
cutaway_cutter();
}
}
module exhaust_headers() {
color(exhaust_color)
difference() {
union() {
// Right bank
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
header_pipe(bank_angle, 1, y_s);
}
// Right Collector
translate([50, 10, -25]) rotate([-90,0,0]) cylinder(r=6, h=100);
// Left bank
for(i=[0:3]) {
y_s = pin_y_start[i] + 10;
header_pipe(-bank_angle, -1, y_s);
}
// Left Collector
translate([-50, 10, -25]) rotate([-90,0,0]) cylinder(r=6, h=100);
}
cutaway_cutter();
}
}
module header_pipe(ang, dir, y) {
gx = dir * (14*cos(45) + (deck_height+7.5)*sin(45));
gz = (deck_height+7.5)*cos(45) - 14*sin(45);
hull() {
translate([gx, y, gz]) rotate([0, 90, 0]) cylinder(r=4, h=2, center=true);
translate([gx + dir*5, y, gz - 5]) sphere(r=4);
}
hull() {
translate([gx + dir*5, y, gz - 5]) sphere(r=4);
translate([dir*50, y, -25]) sphere(r=5);
}
}
module front_accessories() {
color(block_color)
union() {
// Timing cover
hull() {
translate([-25, 120, -10]) cube([50, 4, 20]);
translate([-15, 120, 40]) cube([30, 4, 10]);
}
// Water pump
translate([0, 124, 30]) rotate([-90,0,0]) cylinder(r=12, h=8);
// Water pump pulley
color(pulley_color) translate([0, 132, 30]) rotate([-90,0,0]) {
cylinder(r=10, h=8);
translate([0,0,8]) cylinder(r=8, h=2);
}
}
}
module alternator() {
color("LightGray")
union() {
translate([20, 125, 40]) rotate([-90,0,0]) cylinder(r=8, h=10);
color(pulley_color) translate([20, 135, 40]) rotate([-90,0,0]) cylinder(r=6, h=5);
}
}
module belt() {
color("#222222")
difference() {
hull() {
translate([0, 134, 30]) rotate([-90,0,0]) cylinder(r=10, h=4);
translate([0, 134, 0]) rotate([-90,0,0]) cylinder(r=16, h=4);
translate([20, 135, 40]) rotate([-90,0,0]) cylinder(r=6, h=4);
}
hull() {
translate([0, 133, 30]) rotate([-90,0,0]) cylinder(r=8, h=6);
translate([0, 133, 0]) rotate([-90,0,0]) cylinder(r=14, h=6);
translate([20, 134, 40]) rotate([-90,0,0]) cylinder(r=4, h=6);
}
}
}
module fan() {
color("Silver")
union() {
translate([0, 142, 30]) rotate([-90,0,0]) cylinder(r=5, h=2);
// Fan blades removed per user request
}
}
module front_pulley() {
color(pulley_color)
translate([0, 124, 0]) rotate([-90,0,0]) {
cylinder(r=12, h=8);
translate([0,0,8]) cylinder(r=16, h=8);
}
}
module flywheel() {
color(pulley_color)
translate([0, -5, 0]) rotate([-90,0,0]) cylinder(r=25, h=5);
}
module engine_stand() {
color("Orange")
union() {
// Floor base
translate([-40, -40, -60]) cube([80, 160, 5]);
// Upright
translate([-15, -30, -55]) cube([30, 15, 75]);
// Stand head
translate([-10, -15, -10]) cube([20, 10, 20]);
// Standoff mounts to bell housing
for(x=[-15, 15]) {
for(z=[-5, 15]) {
translate([x, -15, z]) rotate([-90,0,0]) cylinder(r=2, h=15);
}
}
}
}
module internals(crank_angle_offset=0) {
color(internals_color) {
// Front extension connecting to front pulley
translate([0, 120, 0]) rotate([-90,0,0]) cylinder(r=5, h=20);
// Rear extension connecting to flywheel
translate([0, -10, 0]) rotate([-90,0,0]) cylinder(r=5, h=10);
// Main journals nested in bulkheads
for(y = main_y) {
translate([0, y, 0]) {
translate([0, 4, 0]) rotate([-90,0,0]) cylinder(r=5, h=8, center=true);
}
}
for(i = [0:3]) {
ang = crank_angle_offset + pin_angles[i];
y_s = pin_y_start[i];
// Web 1
web(ang, y_s);
// Crank Pin
translate([0, y_s + 5, 0])
rotate([0, ang, 0])
translate([0, 0, crank_r])
translate([0, 5, 0]) rotate([-90,0,0]) cylinder(r=4, h=10, center=true);
// Web 2
web(ang, y_s + 15);
// Piston 1 (Right Bank)
piston_and_rod(ang, bank_angle, y_s + 7.5);
// Piston 2 (Left Bank)
piston_and_rod(ang, -bank_angle, y_s + 12.5);
}
}
}
module web(angle, y_pos) {
translate([0, y_pos, 0])
rotate([0, angle, 0]) {
hull() {
translate([0, 2.5, 0]) rotate([-90,0,0]) cylinder(r=6, h=5, center=true);
translate([0, 2.5, crank_r]) rotate([-90,0,0]) cylinder(r=6, h=5, center=true);
}
// Counterweight
translate([0, 2.5, -crank_r/2 + 1])
cube([16, 5, crank_r + 10], center=true);
}
}
module piston_and_rod(crank_ang, bank_ang, y_pos) {
D = get_D(crank_ang, bank_ang);
Px = crank_r * sin(crank_ang);
Pz = crank_r * cos(crank_ang);
Wx = D * sin(bank_ang);
Wz = D * cos(bank_ang);
rod_ang = atan2(Wx - Px, Wz - Pz);
translate([0, y_pos, 0]) {
// Conrod
translate([Px, 0, Pz])
rotate([0, rod_ang, 0])
translate([0, -2.5, 0]) {
hull() {
rotate([-90,0,0]) cylinder(r=4, h=5);
translate([0, 0, conrod_len]) rotate([-90,0,0]) cylinder(r=3, h=5);
}
}
// Piston pin
translate([Wx, 0, Wz])
rotate([0, bank_ang, 0])
rotate([-90,0,0]) cylinder(r=2.5, h=10, center=true);
// Piston body
translate([Wx, 0, Wz])
rotate([0, bank_ang, 0]) {
translate([0, 0, -6]) rotate([-90,0,0]) cylinder(r=bore-0.5, h=12, center=true);
translate([0, 0, 6]) rotate([-90,0,0]) cylinder(r=bore-0.5, h=4, center=true);
}
}
}
assemble_engine();
+59
View File
@@ -0,0 +1,59 @@
# CADAM Benchmarks
A showcase of what [CADAM](https://adam.new/cadam) builds from a single plain-language
description. Each benchmark starts from the prompt shown and comes out as fully parametric
OpenSCAD — adjustable dimensions and colours, ready to export as `.STL`, `.SCAD`, or `.DXF`.
The `.scad` source for each model is included here, so they double as a record of how well
CADAM turns plain language into real, printable, fully parametric CAD.
### Complex machines & assemblies
| Model | What it shows | Controls |
| --- | --- | --- |
| [V8 engine](13-v8-engine.md) | complete V8 — banks, heads, valve covers, manifold, headers, crank, pistons, oil pan | 22 dims · 8 colors |
| [9-cylinder radial aircraft engine](10-radial-aircraft-engine.md) | nine finned cylinders in a radial star + prop hub | 15 dims · 6 colors |
| [Turbofan jet engine](11-turbofan-jet-engine.md) | full engine — fan, bypass cowl, core stages, exhaust plug | 2 dims · 10 colors |
| [Axial turbine blisk](12-axial-turbine-blisk.md) | ring of twisted aerofoil blades on a keyed hub | 14 dims · 1 color |
### Parametric fundamentals
| Model | What it shows | Controls |
| --- | --- | --- |
| [Twisted hexagonal vase](01-twisted-hex-vase.md) | generative twist-loft, hollow shell with solid floor | 6 dims · 1 color |
| [Knurled control knob](02-knurled-control-knob.md) | diamond knurling, D-bore + set screw, pointer | 15 dims · 2 colors |
| [Hex bolt & nut](03-hex-bolt-and-nut.md) | **real ISO threads** (BOSL2 `screw`/`nut`) | 3 dims · 2 colors |
| [Honeycomb bracket](04-honeycomb-bracket.md) | generative hex lattice, filleted L-bracket | 13 dims · 1 color |
| [NACA 2412 wing](05-naca-airfoil-wing.md) | true airfoil from the NACA equations, tapered loft | 9 dims · 1 color |
| [Threaded jar & lid](06-threaded-jar-and-lid.md) | two **mating** threaded parts | 9 dims · 2 colors |
| [Bevel gear drive](07-bevel-gear-drive.md) | meshing bevel gear pair at 90° | 9 dims · 3 colors |
| [Centrifugal impeller](08-centrifugal-impeller.md) | 7 swept backward-curved blades | 10 dims · 1 color |
| [Planetary gear stage](09-herringbone-planetary-gearbox.md) | full epicyclic assembly, herringbone teeth | 10 dims · 4 colors |
## Regenerating the GIFs
`render.sh` turns any `.scad` into a clean orbiting GIF (and, with `--sheet`, a
4-view contact sheet). It mirrors CADAM's own preview: BOSL2 on the library path,
`color()` parts preserved, a clean orbit around the vertical axis.
Prerequisites (macOS shown; any OpenSCAD ≥ 2021.01 with BOSL2 support works):
```bash
# OpenSCAD CLI + ImageMagick
brew install --cask openscad@snapshot
brew install imagemagick
# BOSL2 (and BOSL) on the OpenSCAD library path — these are bundled in the repo
mkdir -p /tmp/oscad-libs/BOSL2 /tmp/oscad-libs/BOSL
unzip -o ../public/libraries/BOSL2.zip -d /tmp/oscad-libs/BOSL2
unzip -o ../public/libraries/BOSL.zip -d /tmp/oscad-libs/BOSL
```
Then:
```bash
./render.sh 03-hex-bolt-and-nut.scad # -> 03-hex-bolt-and-nut.gif
./render.sh --sheet 09-herringbone-planetary-gearbox.scad # -> *.sheet.png (inspection)
```
Knobs (env vars): `FRAMES` (default 36), `SIZE` (520), `ELEV` (62°), `FPS` (24),
`COLORSCHEME` (Tomorrow), `OPENSCADPATH` (`/tmp/oscad-libs`), `OPENSCAD_BIN`.
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
#
# render.sh — turn a CADAM benchmark .scad into an orbiting GIF (and an optional
# multi-view contact sheet for inspection). Mirrors how CADAM previews models in
# the browser: BOSL2/MCAD on the library path, color() parts preserved, a clean
# orbit around the vertical axis.
#
# Usage:
# ./render.sh model.scad -> model.gif (orbit)
# ./render.sh model.scad out.gif -> out.gif
# ./render.sh --sheet model.scad -> model.sheet.png (iso/front/right/top)
#
# Env knobs (sane defaults):
# FRAMES=36 SIZE=520 ELEV=62 FPS=24 COLORSCHEME=Tomorrow BG=#0d1117
#
set -euo pipefail
# --- locate the OpenSCAD CLI (snapshot/nightly first, then stable) ------------
find_openscad() {
if [[ -n "${OPENSCAD_BIN:-}" && -x "${OPENSCAD_BIN}" ]]; then echo "${OPENSCAD_BIN}"; return; fi
for c in \
"/Applications/OpenSCAD (Nightly).app/Contents/MacOS/OpenSCAD" \
"/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD" \
"$(command -v openscad 2>/dev/null || true)"; do
[[ -n "$c" && -x "$c" ]] && { echo "$c"; return; }
done
echo "ERROR: OpenSCAD CLI not found. Set OPENSCAD_BIN." >&2; exit 1
}
OSCAD="$(find_openscad)"
# BOSL2 / BOSL / MCAD live here (unzipped from public/libraries/*.zip).
export OPENSCADPATH="${OPENSCADPATH:-/tmp/oscad-libs}"
FRAMES="${FRAMES:-36}"
SIZE="${SIZE:-520}"
ELEV="${ELEV:-62}"
FPS="${FPS:-24}"
COLORSCHEME="${COLORSCHEME:-Tomorrow}" # clean near-white backdrop
RENDER_FLAG="${RENDER_FLAG:---render}" # full (manifold) render; --preview is faster but CSG-fuzzy
SHEET=0
if [[ "${1:-}" == "--sheet" ]]; then SHEET=1; shift; fi
SRC="${1:?usage: render.sh [--sheet] model.scad [out.(gif|png)]}"
BASE="$(basename "${SRC%.scad}")"
DIR="$(cd "$(dirname "$SRC")" && pwd)"
OUT="${2:-}"
delay=$(awk "BEGIN{printf \"%.0f\", 100/${FPS}}")
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
render_frame() { # rotx roty rotz file
"$OSCAD" -o "$4" --imgsize="${SIZE},${SIZE}" --projection=perspective \
--colorscheme="$COLORSCHEME" $RENDER_FLAG --viewall --autocenter \
--camera="0,0,0,$1,$2,$3,0" "$SRC" 2>"$tmp/err.log" || {
echo "OpenSCAD failed on $SRC:" >&2; cat "$tmp/err.log" >&2; exit 1; }
}
if [[ "$SHEET" == "1" ]]; then
OUT="${OUT:-$DIR/$BASE.sheet.png}"
render_frame 62 0 25 "$tmp/iso.png"
render_frame 90 0 0 "$tmp/front.png"
render_frame 90 0 90 "$tmp/right.png"
render_frame 0 0 0 "$tmp/top.png"
magick montage "$tmp/iso.png" "$tmp/front.png" "$tmp/right.png" "$tmp/top.png" \
-label '' -tile 2x2 -geometry +6+6 -background white "$OUT"
echo "wrote $OUT"
exit 0
fi
OUT="${OUT:-$DIR/$BASE.gif}"
echo "rendering $FRAMES frames of $BASE ..."
i=0
while [[ $i -lt $FRAMES ]]; do
az=$(awk "BEGIN{printf \"%.2f\", $i*360/$FRAMES}")
printf -v n "%03d" "$i"
render_frame "$ELEV" 0 "$az" "$tmp/f_$n.png"
i=$((i+1))
done
echo "assembling $OUT ..."
magick -delay "$delay" -loop 0 "$tmp"/f_*.png \
-layers OptimizePlus -fuzz 3% -colors 200 "$OUT"
# second pass: strip + max compression
magick "$OUT" -strip -layers Optimize "$OUT"
echo "wrote $OUT ($(du -h "$OUT" | cut -f1))"