Over-Panel Hook
A screw-free hook that hangs over the top edge of a shelf board, headboard, desk side panel or door, giving you a place for bags, headphones or towels. A U clip sits on the board with a little clearance, and two small round pads near the ends of the legs are slightly narrower than the board, so the back leg flexes as you push it on and the clip grips instead of rattling. The front leg runs down the face of the board into a J hook, with an optional upper 45 deg prong for a second item. With the default settings for an 18 mm board it is 59 x 70 x 20 mm. Print it flat on its side with no supports, so the layers follow the profile and the U corners and hook are strong in bending; the 5 mm wall is checked against a 2 kg design load. Board thickness (12-45 mm), grip, clip depth, hook drop, hook opening, width, wall thickness and the number of hooks can all be adjusted.
# Over-Panel Hook - no-screw U clip that hangs over a shelf board, headboard or door
#
# How it is used: push the U clip down over the top edge of a board (shelf board,
# headboard, desk side panel or door). The back leg goes behind the board, the
# front leg runs down the face of the board and ends in one J hook (plus an
# optional upper prong) for bags, headphones or towels. Two small round pads near
# the bottom of the legs are slightly narrower than the board, so the back leg
# flexes a little when you push it on and the clip grips instead of rattling.
# The load hangs in front of the board; the bridge rests on the board top and
# the back-leg pad pushes against the back face, so nothing has to be screwed.
#
# Print orientation: flat on its side, exactly as modeled. The side profile is
# drawn in XY (X = across the board, Y = up) and extruded along Z by the hook
# width, so it lies on Z = 0 with no overhangs and no supports. The layers follow
# the profile, so bending at the U corners and the hook stays inside each layer.
#
# Coordinates: board top at Y = 0, board back face at X = 0, front face at X = board_t.
# The profile is a centerline path with arcs at every bend, thickened to wall_t.
# --- Board ---
@param 12..45 step:0.5 group:"Board" desc:"Board thickness (mm) - measure your board"
board_t = 18
@param 0..1.5 step:0.1 group:"Board" desc:"Grip: how much narrower the pads are than the board (mm)"
grip = 0.5
# --- Clip ---
@param 15..60 step:1 group:"Clip" desc:"Clip depth: back leg length below the board top (mm)"
clip_d = 30
@param 10..40 step:1 group:"Clip" desc:"Hook width (extrusion, mm)"
width = 20
@param 4..8 step:0.5 group:"Clip" desc:"Wall thickness of the whole profile (mm)"
wall_t = 5
# --- Hooks ---
@param 40..150 step:1 group:"Hooks" desc:"Hook drop: board top down to the lower hook seat (mm)"
hook_drop = 60
@param 12..50 step:1 group:"Hooks" desc:"Hook opening: gap between the front leg and the hook tip (mm)"
hook_open = 25
@param choices:[1, 2] group:"Hooks" desc:"Number of hooks (2 adds an upper 45deg prong)"
hooks = 1
# --- Strength check ---
@param 0.5..5 step:0.5 group:"Strength" desc:"Design load on the lower hook (kg)"
load_kg = 2
# --- Fixed design values ---
clr = 0.5 # clearance per side between the legs and the board
ri = 1.5 # inner radius at the U corners (fits common board edge rounding)
pad_r = 2 # radius of the grip pads (round = built-in lead-in)
h = wall_t / 2 # half wall = offset distance of the centerline
# --- Centerline positions ---
xb = -clr - h # back leg centerline
xf = board_t + clr + h # front leg centerline
yt = h # bridge centerline (bridge underside sits on Y = 0)
R = ri + h # centerline radius at the U corners
yb_end = h - clip_d # back leg end (its round cap reaches Y = -clip_d)
Rh = (hook_open + wall_t) / 2 # centerline radius of the J hook bottom
yc = Rh - h - hook_drop # J arc center, so the inner seat is at Y = -hook_drop
tip_h = hook_open * 0.5 # straight tip above the arc center
xtip = xf + 2 * Rh
# --- Main profile: back leg, bridge, front leg, J hook ---
path = wire [
(xb, yb_end),
(xb, yt - R),
arc (xb, yt - R) (xb + R, yt) center:(xb + R, yt - R),
(xf - R, yt),
arc (xf - R, yt) (xf, yt - R) center:(xf - R, yt - R),
(xf, yc),
arc (xf, yc) (xf + Rh, yc - Rh) (xtip, yc),
(xtip, yc + tip_h)
]
main = path | offset h | extrude width
# --- Grip pads: half-round ribs on both inner faces near the leg ends ---
pad_p = (clr * 2 + grip) / 2 # pad protrusion past each inner face
pad_y = -clip_d + wall_t + pad_r # pad center height
pads = union [
circle pad_r at:(-clr - pad_r + pad_p, pad_y) | extrude width,
circle pad_r at:(board_t + clr + pad_r - pad_p, pad_y) | extrude width
]
# --- Optional upper prong: 45deg up and out from the front leg ---
pr_len = Rh * 1.2 # prong centerline length
pr_y0 = yc + Rh + wall_t # prong root on the front leg
pr_dx = pr_len * 0.7071
prong = wire [(xf, pr_y0), (xf + pr_dx, pr_y0 + pr_dx)] | offset h | extrude width
hook = if hooks == 2 then main + pads + prong else main + pads
# --- Checks ---
# Rule: every bending-critical section (U corners, front leg, J bottom) is the
# full wall, and the wall must be >= 4 mm for PLA/PETG.
t_corner = (R + h) - (R - h)
assert t_corner >= 4
assert wall_t >= 4
assert R - h ~ ri # inner corner radius as intended
# Bending stress at the front U corner and at the hook bottom:
# M = F x lever (lever = distance from the front leg to the hook seat center),
# sigma = 6 M / (width x wall^2). Keep it under 15 MPa (PLA ~60 MPa along layers, safety x4).
F = load_kg * 9.81
sigma = 6 * F * Rh / (width * wall_t * wall_t)
assert sigma <= 15
# Fit: legs clear the board, pads are narrower than the board by `grip`.
gap = (xf - h) - (xb + h)
pad_gap = gap - 2 * pad_p
assert gap ~ board_t + 2 * clr tol:0.001
assert pad_gap ~ board_t - grip tol:0.001
assert 2 * pad_r - pad_p < wall_t # pads stay inside the leg outline
# The lower hook hangs below the clip, the upper prong leaves room above the seat.
assert hook_drop >= clip_d + 10
assert tip_h + Rh <= hook_drop # hook tip stays below the board top
assert pr_y0 - h * 1.4142 + hook_drop >= 15 # >= 15 mm under the prong root for straps
assert hook.solids == 1
assert hook.valid
assert hook.bbox.x ~ xtip + h - (xb - h) tol:0.001
assert hook.bbox.max[1] ~ wall_t tol:0.001
assert hook.bbox.min[1] ~ -hook_drop - wall_t tol:0.001
assert hook.bbox.z ~ width
assert hook.bbox.min[2] ~ 0 tol:0.001
print "corner thickness" t_corner
print "bending stress MPa" sigma
print "leg gap" gap
print "pad gap" pad_gap
print "size" hook.bbox
hook