Furniture Leg Cap
A slip-on cap for the end of a chair or table leg that protects the floor and quiets scraping. The socket is slightly larger than the leg, and four thin internal crush ribs are slightly smaller, so the ribs deform as the leg goes in and hold it firmly even if the real leg size is a few tenths off; a 45 deg chamfer at the opening guides the leg in. The default cap fits a 22 mm round leg and measures 27.4 x 27.4 x 19 mm, with a 4 mm thick bottom. Print it bottom down with the opening up and no supports; TPU gives a quiet, non-slip cap, while PLA or PETG works well with the optional recess for a self-adhesive felt pad. Adjustable parameters: round or square leg, leg size (15-50 mm), clearance, rib interference, socket depth, wall and bottom thickness, opening chamfer and the felt recess.
# Furniture Leg Cap - slip-on floor protector for round or square chair and table legs
#
# Use: push the cap onto the end of the leg. The bore is a little larger than
# the leg, and four thin vertical crush ribs inside it are a little smaller, so
# the ribs deform as the leg goes in and hold it firmly even when the real leg
# size is off by a few tenths. A 45 deg chamfer at the opening centers the leg
# and guides it past the ribs. The thick bottom takes the wear; print it in TPU
# for a quiet, non-slip cap, or in PLA/PETG with the felt option and glue a
# self-adhesive felt pad into the recess under the bottom.
#
# Print orientation: as modeled, bottom on the bed and the opening up. The
# walls and ribs are vertical and the chamfers are 45 deg, so no supports.
# With the felt recess the first layer above the recess bridges across it;
# this prints cleanly on most printers, and a slight sag is hidden by the pad.
@param choices:["round", "square"] group:"Leg" desc:"Leg cross section"
shape = "round"
@param 15..50..0.5 group:"Leg" desc:"Leg size: diameter (round) or side (square) (mm)"
leg = 22
@param 0.1..0.6..0.05 group:"Fit" desc:"Clearance per side between the leg and the bore wall (mm)"
clear = 0.3
@param 0.0..0.5..0.05 group:"Fit" desc:"Interference per side at the crush rib tips (mm); more = tighter"
crush = 0.2
@param 8..30 group:"Cap" desc:"Depth of the socket (mm)"
depth = 15
@param 1.6..4..0.2 group:"Cap" desc:"Side wall thickness (mm)"
wall = 2.4
@param 3..8..0.5 group:"Cap" desc:"Bottom thickness (mm)"
floor_t = 4
@param 0.5..2..0.5 group:"Cap" desc:"Opening chamfer (mm)"
lead = 1
@param group:"Felt" desc:"Recess under the bottom for a self-adhesive felt pad"
felt = false
@param 0.5..2..0.1 group:"Felt" desc:"Felt recess depth (mm)"
felt_t = 1.2
# --- Derived sizes ---
bore = leg + clear * 2 # socket inner size
outer = bore + wall * 2 # cap outer size
H = floor_t + depth # overall height
corner_r = 1 # inner corner radius of the square socket
rib_r = 1 # crush rib radius
tip_off = leg / 2 - crush # rib tip distance from the axis
rib_c = tip_off + rib_r # rib center (inside the wall, so it fuses)
rib_h = depth - lead - 1.5 # straight part of the rib
felt_rim = 2 # rim left around the felt recess
def section2d(s) = if shape == "round" then circle (s / 2) else (rect (s - corner_r * 2) (s - corner_r * 2) | offset corner_r)
# --- Body ---
shell_o = section2d(outer) | extrude H
| edges >Z | fillet 0.6
| edges <Z | chamfer 0.4
socket = section2d(bore) | extrude (depth + 1) | translate 0 0 floor_t
mouth = section2d(bore) | extrude (lead + 0.5) draft:-45 | translate 0 0 (H - lead)
cup = shell_o - [socket, mouth]
# --- Four crush ribs on the side walls (mid-side for the square socket), cone tops as lead-in ---
rib1 = cylinder rib_r rib_h | floor
| union (cone rib_r 0.2 1.5 | floor | translate 0 0 rib_h)
| translate rib_c 0 floor_t
ribs = union [rib1 | rotate 0 0 a for a in [0, 90, 180, 270]]
cap0 = cup + ribs
# --- Optional felt recess under the bottom ---
recess = section2d(outer - felt_rim * 2) | extrude (felt_t + 0.5) | translate 0 0 -0.5
cap = if felt then cap0 - recess else cap0
# --- Intent ---
assert floor_t >= 3 # thick, wear-resistant bottom
assert (if felt then floor_t - felt_t >= 2.5 else true) # bottom stays solid above the felt
assert rib_c > bore / 2 # rib center inside the wall: ribs fuse
assert bore / 2 - tip_off >= 0.3 # ribs stand out of the wall
assert wall - lead >= 1 # flat rim left beside the opening chamfer
assert cap.solids == 1
assert cap.bbox.x ~ outer
assert cap.bbox.y ~ outer
assert cap.bbox.z ~ H
assert cap.bbox.min[2] ~ 0 tol:0.01
print "rib tips span (mm)" tip_off * 2
print "bore (mm)" bore
cap