first commit
|
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);
|
||||
}
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
|
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);
|
||||
|
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();
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
|
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();
|
||||
|
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();
|
||||
|
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();
|
||||
|
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();
|
||||
|
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)
|
||||
]);
|
||||
}
|
||||
|
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));
|
||||
}
|
||||
|
After Width: | Height: | Size: 513 KiB |
@@ -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>
|
||||
@@ -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();
|
||||
@@ -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`.
|
||||
@@ -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))"
|
||||