kcl-samples → desk-edge-stopper
desk-edge-stopper

KCL
// Desk-Edge Stopper
// 3-D–printable U-shaped guard that slides onto a desk edge and raises a vertical wall to keep pens, phones, cables, etc. from slipping off the tabletop.
@settings(defaultLengthUnit = mm)
// ──────────────────
// PROFILE PARAMETERS
// ──────────────────
// Edit only the variables in this block
// Everything else updates automatically
// Overall length of the guard measured along the desk edge (X direction)
guardLengthAlongDesk = 200
// Define U-Profile
// Depth of the U-profile. Defines how far the stopper wraps over the desk edge. Measured from front to back. Deeper depth increases grip and stability
uProfileDepthInside = 30
// Inner width of the U-profile. Must match the thickness of the desk, plus a small tolerance (~0.5 mm) to slide on snugly without being too tight.
uProfileWidthInside = 20.5
// Height of the vertical wall that blocks objects from falling. This part rises above the desk surface. Typically between 20–50 mm depending on what you want to stop.
stopWallHeight = 20
// A uniform wall thickness is used across all parts for modeling simplicity; for 3D printing, a relatively thick value (around 1/4 to 1/6 of the profile size, min 5mm) ensures strength and reliable fabrication.
materialThickness = 5
// Total vertical height of the guard from base to top of the stopper wall
totalGuardHeight = stopWallHeight + uProfileWidthInside + materialThickness * 2
// Total front-to-back depth of the guard, including wall thickness, measured from the desk-facing surface to the outermost back face
totalGuardDepth = uProfileDepthInside + materialThickness
// ───────────────
// HOLE PARAMETERS
// ───────────────
// Include three screw holes: one centered for general mounting, and one near each end for desks with side-positioned predrilled holes. Covers common installation cases without overcomplicating the layout.
holeCount = 3
// Distance from desk edge to screw hole center; here set to 13 mm. Best positioned near the middle of the upper U-profile surface to reduce risk of cracking.
holeOffsetFromTheTableEdge = 13
// Convert to offset relative to U-profile geometry
holeOffsetFromTheUprofileEdge = uProfileDepthInside - holeOffsetFromTheTableEdge
// Diameter and radius of each hole
holeDiameter = 4
holeRadius = holeDiameter / 2
// Offset to the profile ends for outermost holes. Should be around one-sixth of the length.
holeOffsetFromGuardEdge = guardLengthAlongDesk / 6
// The spacing between the first and last screw holes equals the total guard length minus two side offsets.
holeSpan = guardLengthAlongDesk - (holeOffsetFromGuardEdge * 2)
// Spacing between holes is calculated by dividing the span (first to last hole) by the number of gaps between them, which is one less than the total hole count.
holeSpacing = holeSpan / (holeCount - 1)
// ───────────
// 3D GEOMETRY
// ───────────
// Begin with a 2D sketch of the cross-section. The internal shape comes first, since it must adapt precisely to desk thickness — the rest builds around it. This keeps the model parametric and resilient to design changes.
guardSketch = startSketchOn(YZ)
guardProfile = startProfile(guardSketch, at = [0, 0])
// Internal shape of the U-profile (bottom and two sides)
|> xLine(length = uProfileDepthInside, tag = $innerFaceOfTheUProfileLeftSide)
|> yLine(length = uProfileWidthInside, tag = $innerFaceOfTheUprofileBottom)
// Upper face of the U-profile — this part rests flat on the tabletop
|> xLine(length = -uProfileDepthInside, tag = $innerFaceOfTheUprofileRightSide)
// Thickness of the U-profile walls
|> yLine(length = materialThickness)
// Outside side of the upper face of the U-profile — side facing the ceiling
|> xLine(length = uProfileDepthInside, tag = $upperFaceOfTheUprofile)
// Height of the border wall
|> yLine(length = stopWallHeight, tag = $innerFaceOfTheWall)
|> xLine(length = materialThickness)
// Front face of the whole guard (wall + u-profile)
|> yLine(length = -totalGuardHeight)
// Bottom face of the whole guard (wall + u-profile)
|> xLine(length = -totalGuardDepth)
// Close the loop / thickness of the U-profile wall
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
guardBody = extrude(guardProfile, length = guardLengthAlongDesk)
// ───────────────
// MOUNTING METHOD
// ───────────────
// This model is specifically designed for screw mounting. It includes three holes—one in the center and one near each end—positioned on the top face of the U-profile to align with existing holes on the upper side of the desk. Alternatives like tape or tight friction are possible, but not the intended method here.
// ──────────────
// HOLE GEOMETRY
// ──────────────
// holes
holeSketch = startSketchOn(guardBody, face = upperFaceOfTheUprofile)
holeProfile = circle(
holeSketch,
center = [
-holeOffsetFromTheUprofileEdge,
holeOffsetFromGuardEdge
],
radius = holeRadius,
)
// Hole pattern runs along the Y axis of the sketch, which corresponds to the length of the guard along the desk edge. X points into the desk.
holeArray = patternLinear2d(
holeProfile,
instances = holeCount,
distance = holeSpacing,
axis = [0, 1],
)
applyHoleCutouts = extrude(holeArray, length = -materialThickness)
// ───────
// FILLETS
// ───────
// To reinforce the connection between intersecting walls, we add fillets to all interior corners. This U-profile has three walls, resulting in two internal corners, and the stopper wall adds a third junction to round.
fillets = fillet(
guardBody,
radius = 1,
tags = [
getCommonEdge(faces = [
innerFaceOfTheUprofileBottom,
innerFaceOfTheUProfileLeftSide
]),
getCommonEdge(faces = [
innerFaceOfTheUprofileRightSide,
innerFaceOfTheUprofileBottom
]),
getCommonEdge(faces = [
innerFaceOfTheWall,
upperFaceOfTheUprofile
])
],
)