diff --git a/hooks.scad b/hooks.scad index 99bdbd09..cba61482 100644 --- a/hooks.scad +++ b/hooks.scad @@ -1,7 +1,6 @@ ////////////////////////////////////////////////////////////////////// // LibFile: hooks.scad // Functions and modules for creating hooks and hook like parts. -// At the moment only one part is supported, a ring hook. // Includes: // include // include @@ -298,3 +297,110 @@ module ring_hook(base_size, hole_z, or, ir, od, id, wall, hole="circle", } +// Module: s_hook() +// Synopsis: Creates an S-shaped hook +// SynTags: Geom +// Topics: Parts, Hooks +// See Also: ring_hook() +// Usage: +// s_hook(or, [sides], [l_shaft=], [r_loop1=], [angle1=], [r_loop2=], [angle2=], ...) [ATTACHMENTS]; +// Description: +// Creates an S-shaped hook by sweeping a cross-section profile along a turtle-graphics path. +// The hook consists of a central shaft with configurable loops on each end. Each loop can +// have an optional straight stem extension and a reverse curl. The cross-section can be a +// regular polygon (controlled by `sides`) or a circle (when `sides` < 3). +// . +// The hook is oriented along the Y axis, centered at the origin. The +Y end has loop1 and +// the -Y end has loop2. By default both loops curve 180 degrees creating a classic S shape. +// . +// End caps are generated using rotate_sweep to close the ends of the swept shape cleanly. +// Arguments: +// or = outside radius of the cross-section shape. Default: 2 +// sides = number of sides for the cross-section polygon. Values less than 3 produce a circle. Default: 6 +// --- +// l_shaft = length of the straight central shaft. Default: 25 +// r_loop1 = radius of the loop at the +Y end. Default: 5 +// angle1 = arc angle in degrees for loop1. Default: 180 +// l_stem1 = length of straight segment after loop1. Default: 0 +// r_curl1 = radius of the reverse curl at the +Y end. Default: 0 +// angle_curl1 = arc angle in degrees for curl1. Default: 0 +// r_loop2 = radius of the loop at the -Y end. Default: 5 +// angle2 = arc angle in degrees for loop2. Default: 180 +// l_stem2 = length of straight segment after loop2. Default: 0 +// r_curl2 = radius of the reverse curl at the -Y end. Default: 0 +// angle_curl2 = arc angle in degrees for curl2. Default: 0 +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: CENTER +// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: 0 +// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: UP +// Example(3D): Default S-hook with hexagonal cross-section +// s_hook(); +// Example(3D): Circular cross-section +// s_hook(sides=0); +// Example(3D): Longer shaft +// s_hook(l_shaft=50); +// Example(3D): Larger loop on +Y end +// s_hook(r_loop1=12); +// Example(3D): Added stem on +Y end +// s_hook(l_stem1=5); +// Example(3D): Stem and curl on -Y end +// s_hook(l_stem2=4, r_curl2=5, angle_curl2=70); +// Example(3D): Extended arc with curl +// s_hook(sides=0, angle1=230, l_stem1=3, r_curl1=3, angle_curl1=90); +// Example(3D): Asymmetric hook +// s_hook(or=3, r_loop1=10, angle1=220, r_loop2=6, angle2=160, l_shaft=30); + +function s_hook(or=2, sides=6, l_shaft=25, + r_loop1=5, angle1=180, l_stem1=0, r_curl1=0, angle_curl1=0, + r_loop2=5, angle2=180, l_stem2=0, r_curl2=0, angle_curl2=0, + anchor=CENTER, spin=0, orient=UP) = no_function("s_hook"); +module s_hook(or=2, sides=6, l_shaft=25, + r_loop1=5, angle1=180, l_stem1=0, r_curl1=0, angle_curl1=0, + r_loop2=5, angle2=180, l_stem2=0, r_curl2=0, angle_curl2=0, + anchor=CENTER, spin=0, orient=UP) +{ + dummy = assert(is_finite(l_shaft) && l_shaft > 0, "l_shaft must be positive") + assert(is_int(sides), "sides must be an integer") + assert(is_finite(or) && or > 0, "or must be positive") + assert(is_finite(r_loop1) && r_loop1 >= 0, "r_loop1 must be non-negative") + assert(is_finite(r_loop2) && r_loop2 >= 0, "r_loop2 must be non-negative") + assert(is_finite(angle1) && angle1 >= 0, "angle1 must be non-negative") + assert(is_finite(angle2) && angle2 >= 0, "angle2 must be non-negative"); + _stem1 = max(l_stem1, 1e-10); + _stem2 = max(l_stem2, 1e-10); + shape = sides > 2 ? regular_ngon(sides, or, align_side=FWD) : circle(or); + path1 = turtle(["setdir", 90, "ymove", l_shaft/2, + "arcleft", r_loop1, angle1, + "move", _stem1, + "arcright", r_curl1, angle_curl1]); + path2 = turtle(["setdir", -90, "ymove", -l_shaft/2, + "arcleft", r_loop2, angle2, + "move", _stem2, + "arcright", r_curl2, angle_curl2]); + all_pts = concat(path1, path2); + bnds = pointlist_bounds(all_pts); + sz = [bnds[1].x - bnds[0].x + 2*or, + bnds[1].y - bnds[0].y + 2*or, + 2*or]; + off = [(bnds[0].x + bnds[1].x)/2, + (bnds[0].y + bnds[1].y)/2, + 0]; + endcap = right_half(shape); + anchors = [ + named_anchor("loop1", point3d(last(path1)), UP), + named_anchor("loop2", point3d(last(path2)), DOWN), + named_anchor("shaft_top", [0, l_shaft/2, 0], BACK), + named_anchor("shaft_bot", [0, -l_shaft/2, 0], FWD), + ]; + attachable(anchor, spin, orient, size=sz, offset=off, anchors=anchors) { + union() { + path_sweep(shape, path1); + path_sweep(shape, path2); + move(last(path1)) rotate_sweep(endcap); + move(last(path2)) rotate_sweep(endcap); + } + children(); + } +} + + +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap