Iris Aperture Box
A round box whose lid is a working six-blade mechanical iris: turn the top ring 40 degrees and the blades swing shut over the 48 mm opening like a camera aperture. The blades are rigid plates stacked one level each, so they never flex or overlap in the same plane; each blade pivots on a post of the aperture plate and is driven by a pin riding in a curved slot of the rotor ring, and the slot is computed from the pin's path so the ring turns smoothly with no dead point. Ten printed parts (cup, aperture plate, six blades, rotor, bezel) plus three M3 x 8 screws; the assembled box measures 112 x 105 x 54 mm with a 35 mm deep cup. Every part prints flat with no supports (set part to print, or to a single part). Adjustable parameters: opening diameter, cup depth, blade thickness, fit clearance, and the iris position shown in the assembled view.
# Iris Aperture Box - a round box whose lid is a six-blade mechanical iris
#
# Turn the top ring and six blades swing shut over the opening, like a camera
# aperture. The blades are rigid plates that never share a plane: blade k lies
# on level k, one blade thickness above blade k-1, so the stack is fully
# determined and every interference can be checked here with assertions
# (blades vs rotor slots, blades vs posts, blade vs blade, blades vs wall).
#
# Parts: cup, aperture plate (with the six pivot posts), six blades (each one
# different: its hub and pin lengths depend on its level), rotor ring with six
# curved slots, bezel ring. Three M3 x 8 screws hold the bezel on the cup.
#
# Mechanism: each blade pivots on a post at radius rp. A pin on the blade hub,
# offset tangentially from the pivot, rides in a curved slot of the rotor. The
# slot is the path of that pin seen from the rotor when the rotor turns by the
# same angle as the blade (opposite sense), so the rotor turns `travel` deg
# from open to closed. The transmission angle stays above 60 deg over the
# whole travel, so the ring turns smoothly with no dead point.
#
# Assembly (from the bottom): drop the plate into the cup so its three keys
# enter the grooves; put blade 0 (the one with the longest hub) on its post,
# then blades 1..5 counter-clockwise; set the rotor on the hubs with each pin
# in a slot; screw the bezel on. The rotor rests on the six hub tops and holds
# the blades flat.
#
# Print orientation: all parts flat as laid out (part = "print"), no supports.
# Axes: Z up. In the mechanism, the plate top is the reference z = 0 and the
# rotor sits at z = hs.
@param choices:["assembled", "print", "cup", "plate", "blades", "rotor", "bezel"] desc:"Output: the assembled box, the print layout, or one part"
part = "assembled"
@param 0..1 step:0.05 desc:"Iris position in the assembled view: 0 = closed, 1 = open"
open = 0.5
@param 44..60 step:2 desc:"Opening diameter when fully open (mm)"
aperture = 48
@param 20..80 step:5 desc:"Cup inner depth (mm)"
depth = 35
@param 1.0..2.0 step:0.2 desc:"Blade thickness (mm)"
blade_t = 1.2
@param 0.2..0.6 step:0.05 desc:"Clearance of moving fits, per side (mm)"
fit = 0.4
# --- Mechanism geometry (tuned at ro = 26 and scaled with the aperture) ---
n = 6
ro = aperture / 2 + 1.5 # blade inner arc radius (overlaps the plate hole by 1.5 mm)
s = ro / 26
rp = 45 * s # pivot radius
hub_r = max(4.5, 5 * s)
rl = rp - hub_r - 0.6 # blade outer arc radius: clears the neighbouring hubs by 0.6
a1 = -15 # blade sector, degrees about the arc center
a2 = 145
travel = 40 # blade swing from open to closed (deg); the rotor turns the same
pin_off = 6 # pin offset from the pivot, tangential (mm)
post_r = 2
pin_r = 1.5
rc = rp + hub_r + 1 # cavity radius (inside of the cup wall)
wall = 2.4
plate_t = 3
hs = n * blade_t + 0.3 # hub height: the rotor rests here
rotor_t = 3
bezel_t = 3
floor_t = 2.4
rout = rc + wall
screw_r = rout + 2.5 # screw circle radius (bosses outside the cup)
boss_r = 4.5
theta = (1 - open) * travel # current blade angle in the assembled view
# --- Cup levels ---
zl = floor_t + depth # ledge top = plate bottom
zp = zl + plate_t # plate top = mechanism z 0
ztop = zp + hs + rotor_t + 0.2 # cup wall top; the bezel sits here
# ===== Cup =====
$shell = (cylinder rout ztop | floor) - (cylinder rc (ztop + 2) | floor | translate 0 0 floor_t)
ledge = workplane XZ | polyline [(rc - 2, zl), (rc + 0.5, zl), (rc + 0.5, zl - 4.5), (rc - 2, zl - 2)] | revolve Z
grooves = union [box 3 6.4 (ztop - zl + 1) | floor | translate rc 0 zl | rotate 0 0 (120 * k) for k in range(3)]
bosses = union [cylinder boss_r ztop | floor | translate screw_r 0 0 | rotate 0 0 (60 + 120 * k) for k in range(3)]
taps = union [cylinder 1.3 7 | floor | translate screw_r 0 (ztop - 7) | rotate 0 0 (60 + 120 * k) for k in range(3)]
cup = $shell + ledge + bosses - grooves - taps
assert cup.solids == 1
assert cup.bbox.z ~ ztop
# ===== Aperture plate with posts (local: plate top at z = 0) =====
plate2d = (circle (rc - 0.2)) - (circle (aperture / 2))
keys = union [rect 2.5 6 at:(rc + 0.05, 0) | rotate (120 * k) for k in range(3)]
plate_body = plate2d + keys | extrude plate_t | translate 0 0 (-plate_t)
spacers = union [cylinder hub_r (k * blade_t) | floor | translate rp 0 0 | rotate 0 0 (360 / n * k) for k in range(1, n)]
posts = union [cylinder post_r hs | floor | translate rp 0 0 | rotate 0 0 (360 / n * k) for k in range(n)]
plate = plate_body + spacers + posts
assert plate.solids == 1
# ===== Blade (local frame: arc center at the origin, pivot at (rp, 0), pin at (rp, pin_off)) =====
sector = sketch [
(ro * cos(a1), ro * sin(a1)),
arc (ro * cos(a1), ro * sin(a1)) (ro * cos(a2), ro * sin(a2)) center:(0, 0),
(rl * cos(a2), rl * sin(a2)),
arc (rl * cos(a2), rl * sin(a2)) (rl * cos(a1), rl * sin(a1)) center:(0, 0),
(ro * cos(a1), ro * sin(a1))
]
bridge = rect (rp - rl + 4) 8 at:((rp + rl - 4) / 2, 0)
tab = wire [(rp, 0), (rp, pin_off)] | offset 3.5
hub2d = circle hub_r at:(rp, 0)
outline = union [sector, bridge, tab, hub2d]
blade2d = outline - (circle (post_r + fit) at:(rp, 0))
assert blade2d.area ~ (outline.area - pi * (post_r + fit) ** 2) tol:0.1%
# Blade k: plate on level k, hub tube up to hs, pin up into the rotor
def blade(k) = ((blade2d | extrude blade_t)
+ (cylinder hub_r (hs - k * blade_t) | floor | translate rp 0 0)
+ (cylinder pin_r (hs + rotor_t - 0.6 - (k + 1) * blade_t) | floor | translate rp pin_off blade_t)
- (cylinder (post_r + fit) (hs + 2) | floor | translate rp 0 -1))
| translate 0 0 (k * blade_t)
assert (blade(0)).solids == 1 and (blade(5)).solids == 1
# All six blades at blade angle th, in mechanism coordinates
def blades_at(th) = union [blade(k) | rotate 0 0 th origin:(rp, 0, 0) | rotate 0 0 (360 / n * k) for k in range(n)]
# ===== Rotor (local: bottom at z = 0) =====
# Pin path seen from the rotor: C(t) = R(t) * (P + pin_off * (-sin t, cos t))
slot_pts = [(rp * cos(travel * i / 8) - pin_off * sin(2 * travel * i / 8),
rp * sin(travel * i / 8) + pin_off * cos(2 * travel * i / 8)) for i in range(9)]
slot = spline slot_pts | offset (pin_r + fit)
slots = union [slot | rotate (360 / n * k) for k in range(n)]
rotor2d = (circle (rc - 0.3)) - (circle (aperture / 2 + 0.5))
ridges = union [box (rl - 2 - (ro + 3)) 2 1.2 | floor | translate ((rl - 2 + ro + 3) / 2) 0 rotor_t | rotate 0 0 (360 / n * k + 30) for k in range(n)]
rotor_body = rotor2d - slots | extrude rotor_t
rotor = rotor_body + ridges
assert rotor.solids == 1
def rotor_at(th) = rotor | rotate 0 0 (-th) | translate 0 0 hs
# ===== Bezel (local: bottom at z = 0) =====
bezel2d = (circle rout) - (circle (rc - 2.5))
ears = union [circle boss_r at:(screw_r * cos(60 + 120 * k), screw_r * sin(60 + 120 * k)) for k in range(3)]
ear_holes = union [circle 1.7 at:(screw_r * cos(60 + 120 * k), screw_r * sin(60 + 120 * k)) for k in range(3)]
bezel = bezel2d + ears - ear_holes | extrude bezel_t
assert bezel.solids == 1
# ===== Checks of the mechanism (independent of `open`) =====
# 1. Closed: the six blade outlines, flattened to one plane, cover the plate hole.
# Open: they stay clear of it.
hole_disc = cylinder (aperture / 2) 1 | floor
cover_closed = union [blade2d | rotate travel origin:(rp, 0) | rotate (360 / n * k) | extrude 1 for k in range(n)]
cover_open = union [blade2d | rotate (360 / n * k) | extrude 1 for k in range(n)]
assert (hole_disc * cover_closed).volume ~ hole_disc.volume tol:0.1%
assert clear(hole_disc, cover_open)
# 2. At open, mid-travel and closed: pins ride in the slots, blades clear the
# posts/spacers and the wall, and no two blades share any volume.
b_open = blades_at(0)
b_mid = blades_at(travel / 2)
b_closed = blades_at(travel)
cup0 = cup | translate 0 0 (-zp)
assert clear(b_open, rotor_at(0))
assert clear(b_mid, rotor_at(travel / 2))
assert clear(b_closed, rotor_at(travel))
assert clear(b_open, plate)
assert clear(b_mid, plate)
assert clear(b_closed, plate)
assert clear(b_open, cup0)
assert clear(b_mid, cup0)
assert clear(b_closed, cup0)
blade_sum = (blade(0)).volume + (blade(1)).volume + (blade(2)).volume + (blade(3)).volume + (blade(4)).volume + (blade(5)).volume
assert b_open.volume ~ blade_sum tol:0.1%
assert b_mid.volume ~ blade_sum tol:0.1%
assert b_closed.volume ~ blade_sum tol:0.1%
assert clear(rotor_at(0), plate)
print "cup outer diameter" (2 * rout) "height" ztop "cavity" (2 * rc)
print "rotor turn (deg)" travel "pivot radius" rp "stack height" hs
# ===== Layouts =====
plate_asm = plate | translate 0 0 zp
blades_asm = blades_at(theta) | translate 0 0 zp
rotor_asm = rotor_at(theta) | translate 0 0 zp
bezel_asm = bezel | translate 0 0 ztop
asm = cup + plate_asm + blades_asm + rotor_asm + bezel_asm
blades_print = union [blade(k) | floor | translate ((k % 3) * 85 - 85) ((k // 3) * 70 - 35) 0 for k in range(n)]
gap = 2 * rout + 12
plate_prt = plate | floor | translate gap 0 0
rotor_prt = rotor | translate 0 gap 0
bezel_prt = bezel | translate gap gap 0
blades_prt = blades_print | translate 0 (-(rout + 85)) 0
$print = cup + plate_prt + rotor_prt + bezel_prt + blades_prt
if part == "assembled" then asm
else if part == "print" then $print
else if part == "cup" then cup
else if part == "plate" then (plate | floor)
else if part == "blades" then blades_print
else if part == "rotor" then rotor
else bezel