diff --git a/.openscad_docsgen_rc b/.openscad_docsgen_rc index e49a310..1e93946 100644 --- a/.openscad_docsgen_rc +++ b/.openscad_docsgen_rc @@ -69,7 +69,7 @@ PrioritizeFiles: walls.scad wiring.scad DefineHeader(BulletList): Side Effects -DefineHeader(Table;Headers=Anchor Name|Position): Extra Anchors +DefineHeader(Table;Headers=Anchor Name|Position): Named Anchors DefineHeader(Table;Headers=Anchor Type|What it is): Anchor Types DefineHeader(Table;Headers=Name|Definition): Terminology DefineHeader(BulletList): Requirements diff --git a/attachments.scad b/attachments.scad index 1cc3685..fe3cd9b 100644 --- a/attachments.scad +++ b/attachments.scad @@ -2957,7 +2957,7 @@ function reorient( axis=UP, override, geom, p=undef -) = +) = assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin)) assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)) @@ -3571,7 +3571,7 @@ function _force_anchor_2d(anchor) = /// geom = The geometry description of the shape. function _find_anchor(anchor, geom) = is_string(anchor)? ( - anchor=="origin"? [anchor, CENTER, UP, 0] + anchor=="origin"? [anchor, CENTER, UP, 0] // Ok that this returns 3d anchor in the 2d case? : let( anchors = last(geom), found = search([anchor], anchors, num_returns_per_match=1)[0] diff --git a/bottlecaps.scad b/bottlecaps.scad index 0a15080..8e398f9 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -31,7 +31,7 @@ include // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "tamper-ring" = Centered at the top of the anti-tamper ring channel. // "support-ring" = Centered at the bottom of the support ring. // Example: @@ -160,7 +160,7 @@ function pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) = // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "inside-top" = Centered on the inside top of the cap. // Examples: // pco1810_cap(); @@ -236,7 +236,7 @@ function pco1810_cap(h, r, d, wall, texture="none", anchor=BOTTOM, spin=0, orien // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "tamper-ring" = Centered at the top of the anti-tamper ring channel. // "support-ring" = Centered at the bottom of the support ring. // Example: @@ -362,7 +362,7 @@ function pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) = // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "inside-top" = Centered on the inside top of the cap. // Examples: // pco1881_cap(); @@ -431,7 +431,7 @@ function pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) = // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "support-ring" = Centered at the bottom of the support ring. // Example: // generic_bottle_neck(); @@ -564,7 +564,7 @@ function generic_bottle_neck( // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "inside-top" = Centered on the inside top of the cap. // Examples: // generic_bottle_cap(thread_depth=2,neck_od=INCH,height=INCH/2); diff --git a/gears.scad b/gears.scad index 658a039..387d8cc 100644 --- a/gears.scad +++ b/gears.scad @@ -1643,7 +1643,7 @@ module ring_gear2d( // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "root" = At the base of the teeth, at the center of rack. // "root-left" = At the base of the teeth, at the left end of the rack. // "root-right" = At the base of the teeth, at the right end of the rack. @@ -1912,7 +1912,7 @@ function rack( // rounding = If true, rack tips and valleys are slightly rounded. Default: true // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "root" = At the height of the teeth, at the center of rack. // "root-left" = At the height of the teeth, at the left end of the rack. // "root-right" = At the height of the teeth, at the right end of the rack. @@ -2363,7 +2363,7 @@ module crown_gear( // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: "pitchbase" // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "pitchbase" = With the base of the pitch cone in the XY plane, centered at the origin. This is the natural height for the gear, and the default anchor. // "apex" = At the pitch cone apex for the bevel gear. // "flattop" = At the top of the flat top of the bevel gear. diff --git a/masks3d.scad b/masks3d.scad index 0774217..aebbf60 100644 --- a/masks3d.scad +++ b/masks3d.scad @@ -249,20 +249,6 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // rounding_edge_mask(l=p.z, r=25); // } // } -// Example: Acute angle -// ang=60; -// difference() { -// pie_slice(ang=ang, h=50, r=100); -// zflip_copy(z=25) -// #rounding_corner_mask(r=20, ang=ang); -// } -// Example: Obtuse angle -// ang=120; -// difference() { -// pie_slice(ang=ang, h=50, r=30); -// zflip_copy(z=25) -// #rounding_corner_mask(r=20, ang=ang); -// } function rounding_edge_mask(l, r, ang=90, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask"); module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, h, height, anchor=CENTER, spin=0, orient=UP, @@ -274,7 +260,7 @@ module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, dummy = assert(all_nonnegative([r1,r2]), "radius/diameter value(s) must be nonnegative") assert(all_positive([length]), "length/l/h/height must be a positive value") assert(is_finite(ang) && ang>0 && ang<180, "ang must be a number between 0 and 180"); - steps = ceil(segs(r)*(180-ang)/360); + steps = ceil(segs(max(r1,r2))*(180-ang)/360); function make_path(r) = let( arc = r==0 ? repeat([0,0],steps+1) @@ -383,8 +369,21 @@ module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, // corner_mask(TOP) // #rounding_corner_mask(r=20); // } -// Example: Acute angle mask -// +// Example(VPR=[71.8,0,345.8],VPT=[57.0174,43.8496,24.5863],VPD=263.435,NoScales): Acute angle +// ang=60; +// difference() { +// pie_slice(ang=ang, h=50, r=100); +// zflip_copy(z=25) +// #rounding_corner_mask(r=20, ang=ang); +// } +// Example(VPR=[62.7,0,5.4],VPT=[6.9671,22.7592,20.7513],VPD=192.044): Obtuse angle +// ang=120; +// difference() { +// pie_slice(ang=ang, h=50, r=30); +// zflip_copy(z=25) +// #rounding_corner_mask(r=20, ang=ang); +// } + function rounding_corner_mask(r, ang, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_corner_mask"); module rounding_corner_mask(r, ang=90, d, style="octa", excess=0.1, anchor=CENTER, spin=0, orient=UP) { diff --git a/math.scad b/math.scad index d59d159..bb5be7a 100644 --- a/math.scad +++ b/math.scad @@ -1374,7 +1374,7 @@ function quadratic_roots(a,b,c,real=false) = // Function: polynomial() -// Synopsis: Calculates a polynomial equation at a given value. +// Synopsis: Evaluate a polynomial at a real or complex value. // Topics: Math, Complex Numbers // See Also: quadratic_roots(), polynomial(), poly_mult(), poly_div(), poly_add(), poly_roots() // Usage: @@ -1394,7 +1394,7 @@ function polynomial(p,z,k,total) = // Function: poly_mult() -// Synopsis: Returns the polynomial result of multiplying two polynomial equations. +// Synopsis: Compute product of two polynomials, returning a polynomial. // Topics: Math // See Also: quadratic_roots(), polynomial(), poly_mult(), poly_div(), poly_add(), poly_roots() // Usage: @@ -1416,7 +1416,7 @@ function poly_mult(p,q) = // Function: poly_div() -// Synopsis: Returns the polynomial quotient and remainder results of dividing two polynomial equations. +// Synopsis: Returns the polynomial quotient and remainder results of dividing two polynomials. // Topics: Math // See Also: quadratic_roots(), polynomial(), poly_mult(), poly_div(), poly_add(), poly_roots() // Usage: @@ -1457,7 +1457,7 @@ function _poly_trim(p,eps=0) = // Function: poly_add() -// Synopsis: Returns the polynomial sum of adding two polynomial equations. +// Synopsis: Returns the polynomial sum of adding two polynomials. // Topics: Math // See Also: quadratic_roots(), polynomial(), poly_mult(), poly_div(), poly_add(), poly_roots() // Usage: @@ -1475,7 +1475,7 @@ function poly_add(p,q) = // Function: poly_roots() -// Synopsis: Returns all complex number roots of the given real polynomial. +// Synopsis: Returns all complex valued roots of the given real polynomial. // Topics: Math, Complex Numbers // See Also: quadratic_roots(), polynomial(), poly_mult(), poly_div(), poly_add(), poly_roots() // Usage: @@ -1563,7 +1563,8 @@ function _poly_roots(p, pderiv, s, z, tol, i=0) = // parts are zero. You can specify eps, in which case the test is // z.y/(1+norm(z)) < eps. Because // of poor convergence and higher error for repeated roots, such roots may -// be missed by the algorithm because their imaginary part is large. +// be missed by the algorithm because error can make their imaginary parts +// large enough to appear non-zero. // Arguments: // p = polynomial to solve as coefficient list, highest power term first // eps = used to determine whether imaginary parts of roots are zero diff --git a/metric_screws.scad b/metric_screws.scad index 556e5f0..695ba90 100644 --- a/metric_screws.scad +++ b/metric_screws.scad @@ -373,7 +373,7 @@ function get_metric_nut_thickness(size) = lookup(size, [ // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "base" = At the base of the head. // "countersunk" = At the head height that would be just barely exposed when countersunk. // Examples: @@ -434,7 +434,7 @@ module generic_screw( // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Extra Anchors: +// Named Anchors: // "base" = At the base of the head. // "countersunk" = At the head height that would be just barely exposed when countersunk. // "shank" = At the bottom start of the unthreaded shank. diff --git a/regions.scad b/regions.scad index e602c91..9175da7 100644 --- a/regions.scad +++ b/regions.scad @@ -314,6 +314,8 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // spin = Rotate this many degrees after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 2D point. Default: "centroid" // atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" +// Named Anchors: +// "origin" = The native position of the region. // Anchor Types: // "hull" = Anchors to the virtual convex hull of the region. // "intersect" = Anchors to the outer edge of the region. @@ -1192,6 +1194,8 @@ function _list_three(a,b,c) = // When called as a function and given a list of regions or 2D polygons, // returns the union of all given regions and polygons. Result is a single region. // When called as the built-in module, makes the union of the given children. +// This function is **much** slower than the native union module acting on geometry, +// so you should only use it when you need a point list for further processing. // Arguments: // regions = List of regions to union. // Example(2D): @@ -1227,6 +1231,8 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) = // takes the first region or polygon and differences away all other regions/polygons from it. The resulting // region is returned. // When called as the built-in module, makes the set difference of the given children. +// This function is **much** slower than the native difference module acting on geometry, +// so you should only use it when you need a point list for further processing. // Arguments: // regions = List of regions or polygons to difference. // Example(2D): @@ -1299,6 +1305,8 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) = // When called as a module, performs a Boolean exclusive-or of up to 10 children. Note that when // the input regions cross each other the exclusive-or operator will produce shapes that // meet at corners (non-simple regions), which do not render in CGAL. +// This function is **much** slower than the native intersection module acting on geometry, +// so you should only use it when you need a point list for further processing. // Arguments: // regions = List of regions or polygons to exclusive_or // Example(2D): As Function. A linear_sweep of this shape fails to render in CGAL. diff --git a/rounding.scad b/rounding.scad index 10c1c76..cf8e59a 100644 --- a/rounding.scad +++ b/rounding.scad @@ -951,6 +951,11 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) // spin = Rotate this many degrees after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 2D point. Default: "centroid" // atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" +// Named Anchors: +// "origin" = The native position of the region. +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the region. +// "intersect" = Anchors to the outer edge of the region. // Example(2D): Basic examples illustrating flat, round, and pointed ends, on a finely sampled arc and a path made from 3 segments. // arc = arc(points=[[1,1],[3,4],[6,3]],n=50); // path = [[0,0],[6,2],[9,7],[8,10]]; @@ -1047,7 +1052,7 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) // right(12) // offset_stroke(path, width=1, closed=true); function offset_stroke(path, width=1, rounded=true, start, end, check_valid=true, quality=1, chamfer=false, closed=false, - atype="hull", anchor, spin, cp="centroid") = + atype="hull", anchor="origin", spin, cp="centroid") = let(path = force_path(path)) assert(is_path(path,2),"path is not a 2d path") let( @@ -1091,7 +1096,7 @@ function offset_stroke(path, width=1, rounded=true, start, end, check_valid=true reverse(slice(right_path,startpath[2],-1-endpath[1])), startpath[0] ) - ) + ) reorient(anchor=anchor, spin=spin, two_d=true, path=pts, extent=atype=="hull", cp=cp, p=pts); function os_pointed(dist,loc=0) = @@ -1374,7 +1379,7 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true, // intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings. // surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments. // surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments. -// Extra Anchors: +// Named Anchors: // "base" = Anchor to the base of the shape in its native position, ignoring any "extra" // "top" = Anchor to the top of the shape in its native position, ignoring any "extra" // "zcenter" = Center shape in the Z direction in the native XY position, ignoring any "extra" @@ -2082,6 +2087,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) = // orient = Vector to rotate top towards after spin (module only) // atype = Select "hull" or "intersect" anchor types. (module only) Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. (module only) Default: "centroid" +// Named Anchors: +// "origin" = The native position of the prism. +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the prism. +// "intersect" = Anchors to the surface of the prism. // Example: Uniformly rounded pentagonal prism // rounded_prism(pentagon(3), height=3, // joint_top=0.5, joint_bot=0.5, joint_sides=0.5); @@ -2792,7 +2802,7 @@ Access to the derivative smoothing parameter? // orient = Vector to rotate top towards after spin (module only) // atype = Select "hull" or "intersect" anchor types. (module only) Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. (module only) Default: "centroid" -// Extra Anchors: +// Named Anchors: // "root" = Root point of the joiner prism, pointing out in the direction of the prism axis // "end" = End point of the joiner prism, pointing out in the direction of the prism axis // Example(3D,NoScales): Here is the simplest case, a circular prism with a specified length standing vertically on a plane. diff --git a/screws.scad b/screws.scad index 172ff07..632459c 100644 --- a/screws.scad +++ b/screws.scad @@ -277,22 +277,22 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc // shaft = screw shaft // shank = unthreaded section of shaft (invalid if screw is fully threaded) // threads = threaded section of screw -// Extra Anchors: -// top = top of screw -// bot = bottom of screw -// center = center of screw -// head_top = top of head (same as top for headless screws) -// head_bot = bottom of head (same as top for headless screws) -// head_center = center of head (same as top for headless screws) -// shaft_top = top of shaft -// shaft_bot = bottom of shaft -// shaft_center = center of shaft -// shank_top = top of shank (invalid if screw is fully threaded) -// shank_bot = bottom of shank (invalid if screw is fully threaded) -// shank_center = center of shank (invalid if screw is fully threaded) -// threads_top = top of threaded portion of screw (invalid if thread_len=0) -// threads_bot = bottom of threaded portion of screw (invalid if thread_len=0) -// threads_center = center of threaded portion of screw (invalid if thread_len=0) +// Named Anchors: +// "top" = top of screw +// "bot" = bottom of screw +// "center" = center of screw +// "head_top" = top of head (same as top for headless screws) +// "head_bot" = bottom of head (same as top for headless screws) +// "head_center" = center of head (same as top for headless screws) +// "shaft_top" = top of shaft +// "shaft_bot" = bottom of shaft +// "shaft_center" = center of shaft +// "shank_top" = top of shank (invalid if screw is fully threaded) +// "shank_bot" = bottom of shank (invalid if screw is fully threaded) +// "shank_center" = center of shank (invalid if screw is fully threaded) +// "threads_top" = top of threaded portion of screw (invalid if thread_len=0) +// "threads_bot" = bottom of threaded portion of screw (invalid if thread_len=0) +// "threads_center" = center of threaded portion of screw (invalid if thread_len=0) // Example(Med): Selected UTS (English) screws // $fn=32; // xdistribute(spacing=8){ @@ -803,22 +803,22 @@ module screw(spec, head, drive, thread, drive_size, // shaft = screw shaft // shank = unthreaded section of shaft (invalid if screw is fully threaded) // threads = threaded section of screw -// Extra Anchors: -// top = top of screw -// bot = bottom of screw -// center = center of screw -// head_top = top of head (invalid for headless screws) -// head_bot = bottom of head (invalid for headless screws) -// head_center = center of head (invalid for headless screws) -// shaft_top = top of shaft -// shaft_bot = bottom of shaft -// shaft_center = center of shaft -// shank_top = top of shank (invalid if screw is fully threaded) -// shank_bot = bottom of shank (invalid if screw is fully threaded) -// shank_center = center of shank (invalid if screw is fully threaded) -// threads_top = top of threaded portion of screw (invalid if thread_len=0) -// threads_bot = bottom of threaded portion of screw (invalid if thread_len=0) -// threads_center = center of threaded portion of screw (invalid if thread_len=0) +// Named Anchors: +// "top" = top of screw +// "bot" = bottom of screw +// "center" = center of screw +// "head_top" = top of head (invalid for headless screws) +// "head_bot" = bottom of head (invalid for headless screws) +// "head_center" = center of head (invalid for headless screws) +// "shaft_top" = top of shaft +// "shaft_bot" = bottom of shaft +// "shaft_center" = center of shaft +// "shank_top" = top of shank (invalid if screw is fully threaded) +// "shank_bot" = bottom of shank (invalid if screw is fully threaded) +// "shank_center" = center of shank (invalid if screw is fully threaded) +// "threads_top" = top of threaded portion of screw (invalid if thread_len=0) +// "threads_bot" = bottom of threaded portion of screw (invalid if thread_len=0) +// "threads_center" = center of threaded portion of screw (invalid if thread_len=0) // Example: Counterbored clearance hole // diff() // cuboid(20) @@ -1037,22 +1037,22 @@ module screw_hole(spec, head, thread, oversize, hole_oversize, head_oversize, // shoulder = the shoulder // shaft = screw shaft // threads = threaded section of screw -// Extra Anchors: -// top = top of screw -// bot = bottom of screw -// center = center of screw -// head_top = top of head (invalid for headless screws) -// head_bot = bottom of head (invalid for headless screws) -// head_center = center of head (invalid for headless screws) -// shoulder_top = top of shoulder -// shoulder_bot = bottom of shoulder -// shoulder_center = center of shoulder -// shaft_top = top of shaft -// shaft_bot = bottom of shaft -// shaft_center = center of shaft -// threads_top = top of threaded portion of screw (invalid if thread_len=0) -// threads_bot = bottom of threaded portion of screw (invalid if thread_len=0) -// threads_center = center of threaded portion of screw (invalid if thread_len=0) +// Named Anchors: +// "top" = top of screw +// "bot" = bottom of screw +// "center" = center of screw +// "head_top" = top of head (invalid for headless screws) +// "head_bot" = bottom of head (invalid for headless screws) +// "head_center" = center of head (invalid for headless screws) +// "shoulder_top" = top of shoulder +// "shoulder_bot" = bottom of shoulder +// "shoulder_center" = center of shoulder +// "shaft_top" = top of shaft +// "shaft_bot" = bottom of shaft +// "shaft_center" = center of shaft +// "threads_top" = top of threaded portion of screw (invalid if thread_len=0) +// "threads_bot" = bottom of threaded portion of screw (invalid if thread_len=0) +// "threads_center" = center of threaded portion of screw (invalid if thread_len=0) // Example: ISO shoulder screw // shoulder_screw("iso",10,length=20); // Example: English shoulder screw diff --git a/shapes2d.scad b/shapes2d.scad index 87da086..9e2a2ee 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -549,7 +549,7 @@ function ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER // align_side = If given as a 2D vector, rotates the whole shape so that the normal of side0 points in that direction. This occurs before spin. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "tip0", "tip1", etc. = Each tip has an anchor, pointing outwards. // "side0", "side1", etc. = The center of each side has an anchor, pointing outwards. // Example(2D): by Outer Size @@ -691,7 +691,7 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, // align_side = If given as a 2D vector, rotates the whole shape so that the normal of side0 points in that direction. This occurs before spin. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "tip0" ... "tip4" = Each tip has an anchor, pointing outwards. // "side0" ... "side4" = The center of each side has an anchor, pointing outwards. // Example(2D): by Outer Size @@ -752,7 +752,7 @@ module pentagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip // align_side = If given as a 2D vector, rotates the whole shape so that the normal of side0 points in that direction. This occurs before spin. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "tip0" ... "tip5" = Each tip has an anchor, pointing outwards. // "side0" ... "side5" = The center of each side has an anchor, pointing outwards. // Example(2D): by Outer Size @@ -812,7 +812,7 @@ module hexagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip, // align_side = If given as a 2D vector, rotates the whole shape so that the normal of side0 points in that direction. This occurs before spin. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "tip0" ... "tip7" = Each tip has an anchor, pointing outwards. // "side0" ... "side7" = The center of each side has an anchor, pointing outwards. // Example(2D): by Outer Size @@ -863,8 +863,8 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip, // --- // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: -// hypot = Center of angled side, perpendicular to that side. +// Named Anchors: +// "hypot" = Center of angled side, perpendicular to that side. // Example(2D): // right_triangle([40,30]); // Example(2D): With `center=true` @@ -1144,7 +1144,7 @@ module trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, ancho // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // atype = Choose "hull" or "intersect" anchor methods. Default: "hull" -// Extra Anchors: +// Named Anchors: // "tip0" ... "tip4" = Each tip has an anchor, pointing outwards. // "pit0" ... "pit4" = The inside corner between each tip has an anchor, pointing outwards. // "midpt0" ... "midpt4" = The center-point between each pair of tips has an anchor, pointing outwards. @@ -1439,7 +1439,7 @@ function teardrop2d(r, ang=45, cap_h, d, circum=false, realign=false, anchor=CEN // d1 = diameter of the left-hand circle // d2 = diameter of the right-hand circle // D = diameter of the joining arcs -// Extra Anchors: +// Named Anchors: // "left" = center of the left circle // "right" = center of the right circle // Example(2D,NoAxes): This first example shows how the egg is constructed from two circles and two joining arcs. @@ -1930,7 +1930,7 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // d = Diameter of the shape. Scale shape to fit in a circle of diameter d. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "tip0", "tip1", etc. = Each tip has an anchor, pointing outwards. // Examples(2D): // reuleaux_polygon(n=3, r=50); @@ -2025,7 +2025,7 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) = // script = The script the text is in. Default: `"latin"` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"baseline"` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` -// Extra Anchors: +// Named Anchors: // "baseline" = Anchors at the baseline of the text, at the start of the string. // str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. // Examples(2D): diff --git a/shapes3d.scad b/shapes3d.scad index dd9e596..d45589a 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -1120,10 +1120,10 @@ function rect_tube( // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // -// Extra Anchors: -// hypot = Center of angled wedge face, perpendicular to that face. -// hypot_left = Left side of angled wedge face, bisecting the angle between the left side and angled faces. -// hypot_right = Right side of angled wedge face, bisecting the angle between the right side and angled faces. +// Named Anchors: +// "hypot" = Center of angled wedge face, perpendicular to that face. +// "hypot_left" = Left side of angled wedge face, bisecting the angle between the left side and angled faces. +// "hypot_right" = Right side of angled wedge face, bisecting the angle between the right side and angled faces. // // Example: Centered // wedge([20, 40, 15], center=true); @@ -2741,10 +2741,10 @@ function torus( // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // -// Extra Anchors: -// cap = The center of the top of the cap, oriented with the cap face normal. -// cap_fwd = The front edge of the cap. -// cap_back = The back edge of the cap. +// Named Anchors: +// "cap" = The center of the top of the cap, oriented with the cap face normal. +// "cap_fwd" = The front edge of the cap. +// "cap_back" = The back edge of the cap. // // Example: Typical Shape // teardrop(r=30, h=10, ang=30); @@ -2862,9 +2862,9 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, chamf // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // -// Extra Anchors: -// cap = The center of the top of the cap, oriented with the cap face normal. -// tip = The position where an un-capped onion would come to a point, oriented in the direction the point is from the center. +// Named Anchors: +// "cap" = The center of the top of the cap, oriented with the cap face normal. +// "tip" = The position where an un-capped onion would come to a point, oriented in the direction the point is from the center. // // Example: Typical Shape // onion(r=30, ang=30); @@ -3365,10 +3365,12 @@ module fillet(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,length, h, height, anc // surface where the height at any given point is the scalar value for that position. // One script to convert a grayscale image to a heightfield array in a .scad file can be found at: // https://raw.githubusercontent.com/BelfrySCAD/BOSL2/master/scripts/img2scad.py +// The bottom value defines a planar base for the resulting shape and it must be strictly less than +// the model data to produce valid geometry, so data which is too small is set to 0.1 units above the bottom value. // Arguments: // data = This is either the 2D rectangular array of heights, or a function literal that takes X and Y arguments. // size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes. Default: `[100,100]` -// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly above this height. Default: -20 +// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly (0.1) above this height. Default: -20 // maxz = The maximum height to model. Truncates anything taller to this height. Set to INF for no truncation. Default: 100 // xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1] // yrange = A range of values to iterate Y over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1] @@ -3437,7 +3439,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04 for (x = [0:1:xcnt-1]) [ size.x * (x/(xcnt-1)-0.5), size.y * (y/(ycnt-1)-0.5), - min(data[y][x],maxz) + min(max(data[y][x],bottom+0.1),maxz) ] ] ] : [ diff --git a/skin.scad b/skin.scad index c864735..00a151e 100644 --- a/skin.scad +++ b/skin.scad @@ -21,7 +21,7 @@ // Synopsis: Connect a sequence of arbitrary polygons into a 3D object. // SynTags: VNF, Geom // Topics: Extrusion, Skin -// See Also: sweep(), linear_sweep(), rotate_sweep(), spiral_sweep(), path_sweep(), offset_sweep() +// See Also: vnf_vertex_array(), sweep(), linear_sweep(), rotate_sweep(), spiral_sweep(), path_sweep(), offset_sweep() // Usage: As module: // skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[atype=]) [ATTACHMENTS]; // Usage: As function: @@ -163,6 +163,8 @@ // atype = Select "hull" or "intersect" anchor types. Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // style = vnf_vertex_array style. Default: "min_edge" +// Named Anchors: +// "origin" = The native position of the shape. // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. @@ -562,7 +564,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. // "bbox" = Anchors to the bounding box of the extruded shape. -// Extra Anchors: +// Named Anchors: // "origin" = Centers the extruded shape vertically only, but keeps the original path positions in the X and Y. Oriented UP. // "original_base" = Keeps the original path positions in the X and Y, but at the bottom of the extrusion. Oriented UP. // Example: Extruding a Compound Region. @@ -884,6 +886,8 @@ function linear_sweep( // anchor = Translate so anchor point is at the origin. Default: "origin" // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) +// Named Anchors: +// "origin" = The native position of the shape. // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. @@ -1492,11 +1496,12 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. -// Extra Anchors: -// start = When `closed==false`, the origin point of the shape, on the starting face of the object -// end = When `closed==false`, the origin point of the shape, on the ending face of the object -// start-centroid = When `closed==false`, the centroid of the shape, on the starting face of the object -// end-centroid = When `closed==false`, the centroid of the shape, on the ending face of the object +// Named Anchors: +// "origin" = The native position of the shape +// "start" = When `closed==false`, the origin point of the shape, on the starting face of the object +// "end" = When `closed==false`, the origin point of the shape, on the ending face of the object +// "start-centroid" = When `closed==false`, the centroid of the shape, on the starting face of the object +// "end-centroid" = When `closed==false`, the centroid of the shape, on the ending face of the object // Example(NoScales): A simple sweep of a square along a sine wave: // path = [for(theta=[-180:5:180]) [theta/10, 10*sin(theta)]]; // sq = square(6,center=true); @@ -1800,11 +1805,14 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); trans_scale = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length, symmetry, last_normal, tangent, uniform, relaxed, caps, style, transforms=true,_return_scales=true); + caps = is_def(caps) ? caps : + closed ? false : true; + fullcaps = is_bool(caps) ? [caps,caps] : caps; transforms = trans_scale[0]; scales = trans_scale[1]; firstscale = is_num(scales[0]) ? 1/scales[0] : [1/scales[0].x, 1/scales[0].y]; lastscale = is_num(last(scales)) ? 1/last(scales) : [1/last(scales).x, 1/last(scales).y]; - vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, caps=caps,style=style); + vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, caps=fullcaps,style=style); shapecent = point3d(centroid(shape)); $sweep_transforms = transforms; $sweep_scales = scales; @@ -2037,6 +2045,8 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0, // orient = Vector to rotate top towards after spin // atype = Select "hull" or "intersect" anchor types. Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// Named Anchors: +// "origin" = The native position of the shape. // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. @@ -2168,6 +2178,8 @@ function _ofs_face_edge(face,firstlen,second=false) = // anchor = Translate so anchor point is at the origin. Default: "origin" // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) +// Named Anchors: +// "origin" = The native position of the shape. // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. @@ -3865,10 +3877,10 @@ function texture(tex, n, border, gap, roughness, inset) = /// 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` /// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -/// Extra Anchors: -/// centroid_top = The centroid of the top of the shape, oriented UP. -/// centroid = The centroid of the center of the shape, oriented UP. -/// centroid_bot = The centroid of the bottom of the shape, oriented DOWN. +/// Named Anchors: +/// "centroid_top" = The centroid of the top of the shape, oriented UP. +/// "centroid" = The centroid of the center of the shape, oriented UP. +/// "centroid_bot" = The centroid of the bottom of the shape, oriented DOWN. function _get_vnf_tile_edges(texture) = let( diff --git a/vnf.scad b/vnf.scad index a2f7ab9..3ef2290 100644 --- a/vnf.scad +++ b/vnf.scad @@ -1063,7 +1063,7 @@ function _slice_3dpolygons(polys, dir, cuts) = // Anchor Types: // "hull" = Anchors to the virtual convex hull of the shape. // "intersect" = Anchors to the surface of the shape. -// Extra Anchors: +// Named Anchors: // "origin" = Anchor at the origin, oriented UP. module vnf_polyhedron(vnf, convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") { vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf; diff --git a/walls.scad b/walls.scad index 1122559..c1748fe 100644 --- a/walls.scad +++ b/walls.scad @@ -9,6 +9,8 @@ ////////////////////////////////////////////////////////////////////// +include + // Section: Walls @@ -16,7 +18,7 @@ // Synopsis: Makes an open cross-braced rectangular wall. // SynTags: Geom // Topics: FDM Optimized, Walls -// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() +// See Also: corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // // Usage: // sparse_wall(h, l, thick, [maxang=], [strut=], [max_bridge=]) [ATTACHMENTS]; @@ -30,9 +32,9 @@ // l = length of strut wall. // thick = thickness of strut wall. // --- -// maxang = maximum overhang angle of cross-braces. -// strut = the width of the cross-braces. -// max_bridge = maximum bridging distance between cross-braces. +// maxang = maximum overhang angle of cross-braces, measured down from vertical. Default: 30 +// strut = the width of the cross-braces. Default: 5 +// max_bridge = maximum bridging distance between cross-braces. Default: 20 // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` @@ -129,7 +131,6 @@ module sparse_wall2d(size=[50,100], maxang=30, strut=5, max_bridge=20, anchor=CE ang = atan(ystep/zstep); len = zstep / cos(ang); - attachable(anchor,spin, two_d=true, size=size) { union() { difference() { @@ -138,8 +139,8 @@ module sparse_wall2d(size=[50,100], maxang=30, strut=5, max_bridge=20, anchor=CE } ycopies(ystep, n=yreps) { xcopies(zstep, n=zreps) { - skew(syx=tan(-ang)) square([(h-strut)/zreps, strut], center=true); - skew(syx=tan( ang)) square([(h-strut)/zreps, strut], center=true); + skew(syx=tan(-ang)) square([(h-strut)/zreps, strut/cos(ang)], center=true); + skew(syx=tan( ang)) square([(h-strut)/zreps, strut/cos(ang)], center=true); } } } @@ -148,6 +149,303 @@ module sparse_wall2d(size=[50,100], maxang=30, strut=5, max_bridge=20, anchor=CE } +// Module: sparse_cuboid() +// Synopsis: Makes an open cross-braced cuboid +// SynTags: Geom +// Topics: FDM Optimized, Walls +// See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut(), cuboid() +// Usage: +// sparse_cuboid(size, [dir], [maxang=], [struct=] +// Description: +// Makes an open rectangular cuboid with X-shaped cross-bracing to reduce the need for material in 3d printing. +// The direction of the cross bracing can be aligned with the X, Y or Z axis. This module can be +// used as a drop-in replacement for {{cuboid()}} if you belatedly decide that your model would benefit from +// the sparse construction. Note that for Z aligned bracing the max_bridge parameter contrains the gaps that are parallel +// to the Y axis, and the angle is measured relative to the X direction. +// Arguments: +// size = The size of sparse wall, a number or length 3 vector. +// dir = direction of holes through the cuboid, must be a vector parallel to the X, Y or Z axes, or one of "X", "Y" or "Z". Default: "Y" +// --- +// maxang = maximum overhang angle of cross-braces, measured down from vertical. Default: 30 +// strut = the width of the cross-braces. Default: 5 +// max_bridge = maximum bridging distance between cross-braces. Default: 20 +// chamfer = Size of chamfer, inset from sides. Default: No chamfering. +// rounding = Radius of the edge rounding. Default: No rounding. +// edges = Edges to mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: all edges. +// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: No edges. +// trimcorners = If true, rounds or chamfers corners where three chamfered/rounded edges meet. Default: `true` +// teardrop = If given as a number, rounding around the bottom edge of the cuboid won't exceed this many degrees from vertical. If true, the limit angle is 45 degrees. Default: `false` +// 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` +// Examples: +// sparse_cuboid([10,20,30], strut=1); +// sparse_cuboid([10,20,30], "Y", strut=1); +// sparse_cuboid([10,20,30], UP, strut=1); +// sparse_cuboid(30, FWD, strut=2, rounding=2, $fn=24); +module sparse_cuboid(size, dir=RIGHT, strut=5, maxang=30, max_bridge=20, + chamfer, + rounding, + edges=EDGES_ALL, + except=[], + except_edges, + trimcorners=true, + teardrop=false, + anchor=CENTER, spin=0, orient=UP) +{ + size = scalar_vec3(size); + dummy1=assert(in_list(dir,["X","Y","Z"]) || is_vector(dir,3), "dir must be a 3-vector or one of \"X\", \"Y\", or \"Z\""); + count = len([for(d=dir) if (d!=0) d]); + dummy2=assert(is_string(dir) || (count==1 && len(dir)<=3), "vector valued dir must have exactly one non-zero component"); + dir = is_string(dir) ? dir + : dir.x ? "X" + : dir.y ? "Y" + : "Z"; + attachable(anchor,spin,orient,size=size){ + intersection(){ + if (dir=="X") + sparse_wall(size.z,size.y,size.x,strut=strut,maxang=maxang, max_bridge=max_bridge); + else if (dir=="Y") + zrot(90) + sparse_wall(size.z,size.x,size.y,strut=strut,maxang=maxang, max_bridge=max_bridge); + else + yrot(90) + sparse_wall(size.x,size.y,size.z,strut=strut,maxang=maxang, max_bridge=max_bridge); + cuboid(size=size, chamfer=chamfer, rounding=rounding,edges=edges, except=except, except_edges=except_edges, + trimcorners=trimcorners, teardrop=teardrop); + } + children(); + } +} + + +// Module: hex_panel() +// Usage: +// hex_panel(shape, wall, spacing, [frame=], [bevel=], [bevel_frame=], [h=|height=|l=|length=], [anchor=], [orient=], [spin=]) +// Description: +// Produces a panel with a honeycomb interior. The panel consists of a frame containing +// a honeycob interior. The frame is laid out in the XY plane with the honeycob interior +// and then extruded to the height h. The shape argument defines the outer bounderies of +// the frame. +// . +// The simplest way to define the frame shape is to give a cuboid size as a 3d vector for +// the shape argument. The h argument is not allowed in this case. With rectangular frames you can supply the +// bevel argument which applies a 45 deg bevel on the specified list of edges. These edges +// can be LEFT, RIGHT, FRONT, or BACK to place a bevel the edge facing upward. You can add +// BOTTOM, as in LEFT+BOT, to get a bevel that faces down. When beveling a separate beveled frame +// is added to the model. You can independently control its thickness by setting `bevel_frame`, which +// defaults to the frame thickness. Note also that `frame` and `bevel_frame` can be set to zero +// to produce just the honeycomb. +// . +// The other option is to provide a 2D path as the shape argument. The path must not intersect +// itself. You must give the height argument in this case and you cannot give the bevel argument. +// The panel is made from a linear extrusion of the specified shape. In this case, anchoring +// is done as usual for linear sweeps. The shape appears by default on its base and you can +// choose "hull" or "intersect" anchor types. +// Arguments: +// shape = 3D size vector or a 2D path +// strut = thickness of hexagonal bracing +// spacing = center-to-center spacing of hex cells in the honeycomb. +// -- +// frame = width of the frame around the honeycomb. Default: same as strut +// bevel = list of edges to bevel on rectangular case when shape is a size vector; allowed options are RIGHT, LEFT, BACK, or FRONT, or those directions with BOTTOM added. Default: [] +// bevel_frame = width of the frame applied at bevels. Default: same as frame +// h / height / l / length = thickness of the panel when shape is a path +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` for rectangular panels, `"zcenter"` for extrusions. +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// atype = Select "hull", "intersect" anchor types. Default: "hull" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// Named Anchors: +// "base" = Anchor to the base of the shape in its native position +// "top" = Anchor to the top of the shape in its native position +// "zcenter" = Center shape in the Z direction in the native XY position (default) +// Anchor Types: +// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. +// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings. +// Examples +// hex_panel([50, 100, 5], strut=1.5, spacing=10); +// hex_panel([50, 100, 5], 1.5, 10, frame = 5); +// hex_panel([50, 100, 5], 5, 10.05); +// hex_panel([50, 100, 5], 1.5, 20, frame = 5); +// hex_panel([50, 100, 5], 1.5, 12, frame = 0); +// hex_panel([50, 100, 5], frame = 10, spacing = 20, strut = 4); +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [LEFT, RIGHT]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 3, bevel = [LEFT, RIGHT, FWD, BACK]); +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [LEFT, RIGHT, FWD+BOTTOM, BACK+BOTTOM]); +// hex_panel([50, 100, 10], 1.5, 10, frame=2, bevel_frame=0, bevel = [FWD, BACK+BOT, RIGHT, LEFT]); +// Example: Triangle +// s = [[0, -40], [0, 40], [60, 0]]; +// hex_panel(s, strut=1.5, spacing=10, h = 10, frame = 5); +// Example: Concave polygon +// s = [[0, -40], [0, 70], [60, 0], [80, 20], [70, -20]]; +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Another concave example +// s = [[0, -40], [0, 40], [30, 20], [60, 40], [60, -40], [30, -20]]; +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Circular panel +// hex_panel(circle(30), 1.5, 10, h = 10, frame = 5); +// Example: More complicated shape +// s = glued_circles(d=50, spread=50, tangent=30); +// hex_panel(s, 1.5, 10, h = 10, frame = 5); +// Example: Care is required when arranging panels vertically for 3d printability. Setting `orient=RIGHT` produces the correct result. +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT); +// zrot(-90)hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD, BACK], anchor = FWD + RIGHT + BOTTOM, orient = RIGHT); +// Example: In this example panels one of the panels is positioned with `orient=FWD` which produces hexagons with 60 deg overhang edges that may not be 3d printable. This example alsu uses `bevel_frame` to thin the material at the corner. +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT); +// hex_panel([100, 50, 10], 1.5, 10, frame = 5, bevel_frame=1, bevel = [LEFT, RIGHT], anchor = FWD + LEFT + BOTTOM, orient = FWD); +// Example: Joining panels with {{attach()}}. In this case panels were joined front beveled edge to back beveled edge, which means the hex structure doesn't align at the joint +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT) +// attach(BACK,FRONT) +// hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel_frame=0, bevel = [FWD, BACK]); +// Example: Joining panels with {{attach()}}. Attaching BACK to BACK aligns the hex structure which looks better. +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK], anchor = BACK + RIGHT + BOTTOM, orient = RIGHT) +// attach(BACK,BACK) +// hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [FWD, BACK]); +module hex_panel( + shape, + strut, + spacing, + frame, + bevel_frame, + h, height, l, length, + bevel = [], + anchor, + orient = UP, cp="centroid", atype="hull", + spin = 0) +{ + frame = first_defined([frame,strut]); + bevel_frame = first_defined([bevel_frame, frame]); + shape = force_path(shape,"shape"); + bevel = is_vector(bevel) ? [bevel] : bevel; + bevOK = len([for(bev=bevel) if (norm([bev.x,bev.y])==1 && (bev.x==0 || bev.y==0) && (bev.z==0 || bev.z==-1)) 1]) == len(bevel); + dummy= + assert(is_finite(strut) && strut > 0, "strut must be positive") + assert(is_finite(frame) && frame >= 0, "frame must be nonnegative") + assert(is_finite(bevel_frame) && bevel_frame >= 0, "bevel_frame must be nonnegative") + assert(is_finite(spacing) && spacing>0, "spacing must be positive") + assert(is_path(shape,2) || is_vector(shape, 3), "shape must be a path or a 3D vector") + assert(len(bevel) == 0 || is_vector(shape, 3), "bevel must be used only on rectangular panels") + assert(is_path(shape) || all_positive(shape), "when shape is a size vector all components must be positive") + assert(bevOK, "bevel list contains an invalid entry") + assert(!(in_list(FRONT, bevel) && in_list(FRONT+BOTTOM, bevel)), "conflicting FRONT bevels") + assert(!(in_list(BACK, bevel) && in_list(BACK+BOTTOM, bevel)), "conflicting BACK bevels") + assert(!(in_list(RIGHT, bevel) && in_list(RIGHT+BOTTOM, bevel)), "conflicting RIGHT bevels") + assert(!(in_list(LEFT, bevel) && in_list(LEFT+BOTTOM, bevel)), "conflicting LEFT bevels") + assert(is_undef(h) || is_path(shape), "cannot give h with a size vector"); + shp = is_path(shape) ? shape : square([shape.x, shape.y], center = true); + ht = is_path(shape) ? one_defined([h,l,height,length],"height,length,l,h") + : shape.z; + + bounds = pointlist_bounds(shp); + sizes = bounds[1] - bounds[0]; // [xsize, ysize] + assert(frame*2 + spacing < sizes[0], "There must be room for at least 1 cell in the honeycomb"); + assert(frame*2 + spacing < sizes[1], "There must be room for at least 1 cell in the honeycomb"); + + bevpaths = len(bevel)==0 ? [] + : _bevelSolid(shape,bevel); + if (len(bevel) > 0) { + size1 = [bevpaths[0][0].x-bevpaths[0][1].x, bevpaths[0][2].y-bevpaths[0][1].y,ht]; + size2 = [bevpaths[1][0].x-bevpaths[1][1].x, bevpaths[1][2].y-bevpaths[1][1].y]; + shift = point2d(centroid(bevpaths[1])-centroid(bevpaths[0])); + offset = (centroid(bevpaths[0])); + attachable(anchor,spin,orient,size=size1,size2=size2,shift=shift,offset=offset){ + down(ht/2) + intersection() { + union() { + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + for (b = bevel) _bevelWall(shape, b, bevel_frame); + } + vnf_polyhedron(vnf_vertex_array(bevpaths, col_wrap=true, caps=true)); + } + children(); + } + } + else if (is_vector(shape)){ + attachable(anchor = anchor, spin = spin, orient = orient, size = shape) { + down(ht/2) + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + children(); + } + } + else { + anchors = [ + named_anchor("zcenter", [0,0,0], UP), + named_anchor("base", [0,0,-ht/2], UP), + named_anchor("top", [0,0,ht/2], UP) + ]; + attachable(anchor = default(anchor,"zcenter"), spin = spin, orient = orient, path=shp, h=ht, cp=cp, extent=atype=="hull",anchors=anchors) { + down(ht/2) + linear_extrude(height = ht, convexity=8) { + _honeycomb(shp, spacing = spacing, hex_wall = strut); + offset_stroke(shp, width=[-frame, 0], closed=true); + } + children(); + } + + } +} + + +module _honeycomb(shape, spacing=10, hex_wall=1) +{ + hex = hexagon(id=spacing-hex_wall, spin=180/6); + bounds = pointlist_bounds(shape); + size = bounds[1] - bounds[0]; + hex_rgn2 = grid_copies(spacing=spacing, size=size, stagger=true, p=hex); + center = (bounds[0] + bounds[1]) / 2; + hex_rgn = move(center, p=hex_rgn2); + difference(){ + polygon(shape); + region(hex_rgn); + } +} + + +function _bevelSolid(shape, bevel) = + let( + tX = in_list(RIGHT, bevel) ? -shape.z : 0, + tx = in_list(LEFT, bevel) ? shape.z : 0, + tY = in_list(BACK, bevel) ? -shape.z : 0, + ty = in_list(FRONT, bevel) ? shape.z : 0, + bX = in_list(RIGHT + BOTTOM, bevel) ? -shape.z : 0, + bx = in_list(LEFT + BOTTOM, bevel) ? shape.z : 0, + bY = in_list(BACK + BOTTOM, bevel) ? -shape.z : 0, + by = in_list(FRONT + BOTTOM, bevel) ? shape.z : 0, + pathB = path3d(rect(select(shape,0,1)) + [[bX,by],[bx,by],[bx,bY],[bX,bY]]), + pathT = path3d(rect(select(shape,0,1)) + [[tX,ty],[tx,ty],[tx,tY],[tX,tY]],shape.z) + ) + [pathB,pathT]; + +module _bevelWall(shape, bevel, thickness) { + + l = bevel.y != 0 ? shape.x : shape.y; + d = bevel.y != 0 ? shape.y : shape.x; + zr = bevel.y == -1 ? 180 + : bevel.y == 1 ? 0 + : bevel.x == -1 ? 90 + : bevel.x == 1 ? 270 + : undef; + xr = bevel.x != 0 && bevel.z < 0 ? 180 : 0; + yr = bevel.y != 0 && bevel.z < 0 ? 180 : 0; + + path = [[-thickness, 0], [0, 0], [-shape.z, -shape.z], [-shape.z-thickness, -shape.z]]; + + up(shape.z/2) + xrot(xr) yrot(yr) zrot(zr) down(shape.z/2) + back(d/2) right(l/2) + zrot(90) xrot(-90) + linear_extrude(l) polygon(path); +} + + // Module: corrugated_wall() // Synopsis: Makes a corrugated rectangular wall. // SynTags: Geom @@ -166,7 +464,7 @@ module sparse_wall2d(size=[50,100], maxang=30, strut=5, max_bridge=20, anchor=CE // l = length of strut wall. // thick = thickness of strut wall. // --- -// strut = the width of the cross-braces. +// strut = the width of the frame. // wall = thickness of corrugations. // 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 after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`