← Docs

Common patterns

Copy-paste recipes for everyday 3D printing geometry.

Rounded box / enclosure base

mod rbox(w, d, h, r) {
  hull {
    translate(r, r, 0) cylinder(h: h, r: r)
    translate(w - r, r, 0) cylinder(h: h, r: r)
    translate(r, d - r, 0) cylinder(h: h, r: r)
    translate(w - r, d - r, 0) cylinder(h: h, r: r)
  }
}

rbox(60, 40, 20, 3)

Screw hole with countersink

mod screw_hole(d, h, cs_d, cs_depth) {
  // Through hole
  cylinder(h: h, r: d / 2)
  // Countersink
  translate(0, 0, h - cs_depth)
    cylinder(h: cs_depth + 0.1, r1: d / 2, r2: cs_d / 2)
}

// M3 countersunk screw through 5mm plate
difference {
  cube(20, 20, 5)
  translate(10, 10, -0.5)
    screw_hole(3.4, 6, 6.5, 1.8)
}

Mounting hole pattern

mod mount_holes(w, d, h, hole_r, inset) {
  for (x in [inset, w - inset]) {
    for (y in [inset, d - inset]) {
      translate(x, y, -0.5)
        cylinder(h: h + 1, r: hole_r)
    }
  }
}

difference {
  cube(60, 40, 3)
  mount_holes(60, 40, 3, 1.7, 5)
}

Wall with fillet (rounded inner edge)

mod fillet(r, h) {
  difference {
    cube(r, r, h)
    translate(r, r, -0.1)
      cylinder(h: h + 0.2, r: r)
  }
}

// Vertical wall with fillet at the base
cube(40, 2, 20)
fillet(3, 20)

Circular array

param count: int = 8 [3:24] "Number of items"
param radius: float = 25 [10:50] "Array radius (mm)"

for (i in [0:count - 1]) {
  let angle = i * 360 / count
  rotate(0, 0, angle)
    translate(radius, 0, 0)
      cylinder(h: 10, r: 3)
}

Linear array with spacing

param count: int = 5 [1:20] "Count"
param spacing: float = 15 [8:30] "Spacing (mm)"

for (i in [0:count - 1]) {
  translate(i * spacing, 0, 0) cube(10, 10, 10)
}

Hollow box (shell)

param w: float = 60 [20:150] "Width (mm)"
param d: float = 40 [20:100] "Depth (mm)"
param h: float = 25 [10:60] "Height (mm)"
param wall: float = 2 [1.2:4:0.2] "Wall (mm)"

difference {
  cube(w, d, h)
  translate(wall, wall, wall)
    cube(w - wall*2, d - wall*2, h)
}

Chamfered edge

// 45-degree chamfer on top edge of a box
param size: float = 30 [10:60] "Size (mm)"
param chamfer: float = 2 [0.5:5] "Chamfer (mm)"

difference {
  cube(size, size, size)
  translate(0, 0, size)
    rotate(45, 0, 0)
      cube(size + 1, chamfer * 2, chamfer * 2)
}

Text emboss / deboss

// Raised text on a plate
// Note: text() is supported but limited to built-in fonts
param text_depth: float = 1 [0.4:3:0.2] "Text height (mm)"

cube(50, 20, 2)
translate(5, 5, 2)
  extrude(text_depth)
    text("HELLO", size: 10)

Snap-fit clip

// Simple cantilever snap-fit
param length: float = 10 [5:20] "Clip length (mm)"
param width: float = 5 [3:10] "Clip width (mm)"
param thickness: float = 1 [0.8:2:0.1] "Thickness (mm)"
param hook: float = 1.5 [0.5:3:0.5] "Hook depth (mm)"

// Cantilever arm
cube(length, width, thickness)
// Hook at the end
translate(length - thickness, 0, -hook)
  cube(thickness, width, hook + thickness)

Cable comb (repeated slots)

param cables: int = 6 [2:12] "Number of cables"
param cable_d: float = 5 [3:10:0.5] "Cable diameter (mm)"
param wall: float = 2 [1.5:4:0.5] "Wall (mm)"
param depth: float = 8 [5:15] "Depth (mm)"

let slot = cable_d + wall
let total_w = cables * slot + wall

difference {
  cube(total_w, depth, cable_d + wall * 2)
  for (i in [0:cables - 1]) {
    translate(wall + i * slot + slot / 2, -0.5, wall + cable_d / 2)
      rotate(-90, 0, 0)
        cylinder(h: depth + 1, r: cable_d / 2)
  }
}

Tolerance test block

// Print this to calibrate your clearance parameter
let sizes = [0.1, 0.2, 0.3, 0.4, 0.5]

for (i in [0:4]) {
  translate(i * 12, 0, 0) {
    difference {
      cube(10, 10, 5)
      translate(5, 5, -0.5)
        cylinder(h: 6, r: 3 + sizes[i])
    }
  }
}