Vertical Laptop Stand
A solid desk stand that holds a closed laptop upright on its long edge to free desk space. The slot is the laptop thickness plus play, with a chamfered mouth for easy insertion and a rounded floor, and a window through the slot floor lets air and a charging cable through. The base depth is checked against the laptop's centre of mass: the file computes the lean at which the loaded stand would tip and requires it to exceed the chosen angle, so a deeper laptop asks for a deeper base. The default fits a 16 mm laptop and measures 120 x 70 x 40 mm. Prints flat on its base, slot up, with no supports. Adjustable parameters: laptop thickness and depth, slot play, base width and depth, slot depth and the required tip-over lean.
# Vertical Laptop Stand - holds a closed laptop on edge, base depth checked against tip-over
#
# A solid base with one slot for a closed laptop standing on its long edge. The slot is
# the laptop thickness plus play, with a chamfered mouth for easy insertion and a rounded
# floor; a window through the slot floor lets air and a cable pass. The base depth is
# checked against the laptop's centre of mass: the stand must not tip when the laptop
# leans by `tilt` degrees. Prints flat on its base, slot up, with no supports.
@param 8..40 step:0.5 group:"Laptop" desc:"Laptop thickness when closed (mm)"
$thick = 16
@param 150..400 step:5 group:"Laptop" desc:"Laptop depth when closed (mm): its height when standing on edge"
$lap_depth = 220
@param 1..4 step:0.5 group:"Laptop" desc:"Play added to the slot (mm)"
$play = 2
@param 80..200 step:5 group:"Base" desc:"Base width along the laptop (mm)"
$width = 120
@param 40..120 step:5 group:"Base" desc:"Base depth across the laptop (mm)"
$depth = 70
@param 20..50 step:1 group:"Base" desc:"Slot depth (mm)"
$slot_d = 30
@param 5..20 step:1 group:"Base" desc:"Lean the loaded stand must survive without tipping (deg)"
$tilt = 12
$h = $slot_d + 10 # base height: 10 mm floor under the slot
$slot_w = $thick + $play
$win_w = 36 # window through the slot floor
$base = box $width $depth $h center:(true, true, false) | edges =Z | fillet 6 | edges >Z | chamfer 1.5
$slot = box ($width + 2) $slot_w ($slot_d + 1) center:(true, true, false) | edges =X | fillet 3
| translate 0 0 ($h - $slot_d)
# chamfered mouth: a trapezoid prism along X, 4 mm deep, 4 mm wide on each side
$chamfer_cut = polyline [(-$slot_w / 2 - 4, $h + 0.5), ($slot_w / 2 + 4, $h + 0.5), ($slot_w / 2 + 4, $h), ($slot_w / 2, $h - 4), (-$slot_w / 2, $h - 4), (-$slot_w / 2 - 4, $h)]
| extrude ($width + 2) | rotate 90 0 90 | translate (-$width / 2 - 1) 0 0
$window = box $win_w ($slot_w - 2) ($h + 2) center:(true, true, false) | translate 0 0 -1
$stand = $base - $slot - $chamfer_cut - $window
# --- Tip-over: the laptop's centre of mass sits half its depth above the slot floor ---
$com_h = ($h - $slot_d) + $lap_depth / 2
$half_base = $depth / 2 - 6 # the fillet rounds the corners; keep a margin
$lean = atan($half_base / $com_h) # lean at which the mass passes over the base edge
assert $lean >= $tilt
print "tips over at lean (deg)" $lean
assert $stand.solids == 1
assert $stand.bbox.x ~ $width
assert $stand.bbox.y ~ $depth
assert $stand.bbox.z ~ $h
assert $stand.volume < $base.volume - $slot_w * $width * $slot_d * 0.9 # the slot was cut
assert $chamfer_cut.bbox.x ~ ($width + 2) # the mouth chamfer runs the full length
assert $chamfer_cut.bbox.y ~ ($slot_w + 8)
print "stand" $stand.bbox
print "slot width (mm)" $slot_w
$stand