Part IV: Applied Math & Engineering

Applied Math & Engineering with OpenSCAD

A 3D printer becomes a math and engineering lab when paired with a code-based modeler. OpenSCAD turns geometry into programming — every shape is an equation, every position is a vector.

Install

sudo pacman -S openscad

Working with OpenSCAD

GUI Mode — Interactive Learning

# Open a .scad file in the GUI (code left, 3D preview right)
openscad ~/3dprints/scad/quijote.scad
  • Edit code on the left panel

  • F5 — quick preview (fast, approximate)

  • F6 — full render (slow, exact — required before export)

  • Ctrl+Shift+E — export STL from GUI

Change a number, hit F5, see the result instantly. That’s the learning loop.

Terminal Mode — Render and Validate

# Render .scad to .stl from command line (no GUI)
openscad -o output.stl source.scad

# Render + validate Z min in one pipeline
openscad -o model.stl source.scad && python3 -c "
with open('model.stl','r') as f:
    zvals=[float(l.strip().split()[-1]) for l in f if 'vertex' in l]
print(f'Z min: {min(zvals):.3f}  Z max: {max(zvals):.3f}')
print('BED OK' if min(zvals) >= 0 else 'ERROR: model below bed')
"

Edit with Your Text Editor

OpenSCAD files are plain text. Edit with nvim or any editor, then render from terminal:

nvim ~/3dprints/scad/quijote.scad
openscad -o ~/3dprints/quijote/quijote.stl ~/3dprints/scad/quijote.scad

Full Pipeline: Code → STL → G-code → Print

# 1. Edit the .scad file
nvim source.scad

# 2. Render to STL
openscad -o model.stl source.scad

# 3. Validate
python3 -c "
with open('model.stl','r') as f:
    zvals=[float(l.strip().split()[-1]) for l in f if 'vertex' in l]
print(f'Z min: {min(zvals):.3f}  Z max: {max(zvals):.3f}')
"

# 4. Load in OrcaSlicer → Slice plate → Export G-code

# 5. Print via pronsole
pronsole.py
# > connect /dev/ender3v3se 115200
# > settemp 210
# > load /full/path/to/model.gcode
# > print
# > monitor

Why OpenSCAD (Not a GUI Modeler)

  • Every operation maps to a math concept — no hiding behind click-and-drag

  • Scripts are reproducible, versionable, and teachable

  • Parametric design teaches variables and functions before "programming" ever comes up

  • The jump from OpenSCAD to Python/NumPy is almost trivial — the mental model transfers

The Math You’re Actually Doing

OpenSCAD Operation Math Concept Grade Level

translate([x, y, z])

Vector addition / translation matrix

Middle school

rotate([a, b, c])

Rotation matrices, trigonometry

High school

scale([sx, sy, sz])

Scalar multiplication, linear transforms

Middle school

hull() of two shapes

Convex hull — computational geometry

College intro

intersection() / difference()

Boolean set operations (union, intersection, complement)

Middle school

for (i = [0:10]) with rotate

Polar coordinates, parametric curves

High school

sin(), cos() in position calculations

Trigonometric functions — unit circle

High school

Nesting translate inside rotate

Matrix multiplication, transform composition

Linear algebra

Starter Projects by Skill Level

Beginner — Geometric Solids

Print the five Platonic solids. Measure faces, edges, vertices. Verify Euler’s formula: V - E + F = 2.

// Geometric Solids — Platonic solid exploration
// Print these, count faces/edges/vertices, verify Euler's formula: V - E + F = 2

// Cube — 6 faces, 8 vertices, 12 edges
cube([20, 20, 20], center=true);

// Sphere approximated by polyhedron
translate([40, 0, 0])
    sphere(r=15, $fn=64);

// Cylinder — introduce pi, circumference, area
translate([80, 0, 0])
    cylinder(h=30, r=10, $fn=6);  // hexagonal prism

translate([120, 0, 0])
    cylinder(h=30, r=10, $fn=64); // smooth cylinder

The $fn parameter directly teaches polygon approximation of curves — how a circle is really just a polygon with enough sides.

Intermediate — Parametric Comb

A functional comb — pure geometry, immediately useful:

// Parametric Comb — change variables, everything adapts
// Print on side, 100% infill. PETG for daily use, PLA for prototyping.

teeth       = 30;
tooth_width = 1.2;
tooth_height = 25;
tooth_gap   = 1.5;
base_height = 8;
base_depth  = 4;

// Base bar
cube([teeth * (tooth_width + tooth_gap), base_depth, base_height]);

// Teeth — each one is a for-loop iteration
for (i = [0:teeth-1])
    translate([i * (tooth_width + tooth_gap), 0, base_height])
        cube([tooth_width, base_depth, tooth_height]);

Change one variable, the whole comb adapts. Print at 100% infill. PETG for durability, PLA for prototyping.

Intermediate — Metric Ruler

A precision measuring tool that validates your printer’s dimensional accuracy:

// Metric Ruler — precision test for your printer
// Print at 100% infill, 0.12mm layer height for sharp tick marks.
// Measure against a real ruler to validate dimensional accuracy.

length = 200;  // mm
width  = 25;
height = 2;

difference() {
    cube([length, width, height]);

    // Centimeter marks — tall
    for (i = [0:10:length])
        translate([i, 0, height-1.5])
            cube([0.4, 8, 1.5]);

    // Half-centimeter marks — medium
    for (i = [5:10:length])
        translate([i, 0, height-1.0])
            cube([0.3, 5, 1.0]);

    // Millimeter marks — short
    for (i = [1:1:length])
        if (i % 5 != 0)
            translate([i, 0, height-0.6])
                cube([0.2, 3, 0.6]);

    // Numbers at each centimeter
    for (i = [1:length/10])
        translate([i*10-2, 10, height-0.5])
            linear_extrude(height=0.5)
                text(str(i), size=5, font="Liberation Sans");
}

Print it, measure it against a real ruler. The difference is your printer’s dimensional error.

Intermediate — Parametric Box with Lid

One variable changes everything:

// Parametric Box with Lid — change one variable, everything adapts
// Teaches: variables, expressions, spatial reasoning, tolerance

box_width  = 40;
box_depth  = 30;
box_height = 25;
wall       = 2;
clearance  = 0.3;  // lid clearance — why does this matter?

// Box
difference() {
    cube([box_width, box_depth, box_height]);
    translate([wall, wall, wall])
        cube([box_width - 2*wall,
              box_depth  - 2*wall,
              box_height - wall]);
}

// Lid — offset for printing, includes clearance
translate([box_width + 10, 0, 0])
    cube([box_width + clearance, box_depth + clearance, wall]);

This teaches: variables, expressions, spatial reasoning, tolerance (the lid needs clearance — why?).

Advanced — Gears and Trigonometry

// Involute Gear — every tooth position is sin/cos
// Print two with different tooth counts, mesh them, demonstrate gear ratios

module gear(teeth=20, pitch_radius=20, height=5) {
    tooth_angle = 360 / teeth;
    difference() {
        cylinder(h=height, r=pitch_radius + 2, $fn=teeth*4);
        for (i = [0:teeth-1]) {
            rotate([0, 0, i * tooth_angle + tooth_angle/4])
                translate([pitch_radius, 0, -1])
                    cylinder(h=height+2, r=pitch_radius * sin(tooth_angle/4), $fn=16);
        }
    }
    // Center axle hole
    difference() {
        cylinder(h=height, r=pitch_radius + 2, $fn=teeth*4);
        translate([0, 0, -1])
            cylinder(h=height+2, r=3, $fn=32);
    }
}

// 24-tooth gear
gear(teeth=24, pitch_radius=25, height=8);

// 12-tooth gear — 2:1 ratio
translate([55, 0, 0])
    gear(teeth=12, pitch_radius=12.5, height=8);

Print two meshing gears. Physically demonstrate gear ratios — the math becomes something you can hold and turn.

Advanced — Display Dagger

hull() tapering a diamond cross-section to a point:

// Display Dagger — hull() tapering a diamond cross-section to a point
// Teaches: cross-sections, tapering, symmetry, Boolean operations

module dagger() {
    // Blade — tapered diamond cross-section
    hull() {
        scale([1, 0.3, 1]) cylinder(h=1, r=8, $fn=4);
        translate([0, 0, 100])
            scale([1, 0.3, 1]) cylinder(h=1, r=0.5, $fn=4);
    }
    // Guard — cross piece
    translate([0, 0, -2])
        cube([40, 4, 4], center=true);
    // Handle — octagonal for grip
    translate([0, 0, -35])
        cylinder(h=33, r=5, $fn=8);
    // Pommel
    translate([0, 0, -38])
        sphere(r=6, $fn=32);
}

dagger();

Engineering — Load Testing

  1. Design a bridge in OpenSCAD (parametric: span, thickness, truss pattern)

  2. Print three variants with different infill (15%, 50%, 100%)

  3. Load-test each with weights until failure

  4. Record and graph: weight vs. deflection vs. material used

  5. Iterate the design — this is the actual engineering method

Teaching Workflow

1. Pose a problem     → "Design a phone stand for this specific phone"
2. Sketch on paper    → dimensions, angles, constraints
3. Code in OpenSCAD   → translate the sketch to transforms and primitives
4. Slice in OrcaSlicer → learn about layer height, infill, supports
5. Print              → confront reality (tolerance, bridging, warping)
6. Measure and compare → calipers vs. design intent
7. Iterate            → the gap between design and reality IS engineering

Every cycle through this loop reinforces: spatial reasoning, trigonometry, algebra, precision, and the scientific method. The printer makes the feedback loop physical and immediate — a wrong calculation isn’t a red X on a test, it’s a part that doesn’t fit.