diff --git a/attachments.scad b/attachments.scad new file mode 100644 index 0000000..2f4623f --- /dev/null +++ b/attachments.scad @@ -0,0 +1,397 @@ +////////////////////////////////////////////////////////////////////// +// LibFile: attachments.scad +// This is the file that handles attachments and orientation of children. +// To use, add the following lines to the beginning of your file: +// ``` +// include +// ``` +////////////////////////////////////////////////////////////////////// + +/* +BSD 2-Clause License + +Copyright (c) 2017-2019, Revar Desmera +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +// Section: Functions + + +// Function: connector() +// Usage: +// connector(name, pos, dir, [rot]) +// Description: +// Creates a connector data structure. +// Arguments: +// name = The string name of the connector. Lowercase. Words separated by single dashes. No spaces. +// pos = The [X,Y,Z] position of the connector. +// dir = A vector pointing in the direction parts should project from the connector position. +// rot = If needed, the angle to rotate the part around the direction vector. +function connector(name, pos=[0,0,0], dir=UP, rot=0) = [name, pos, dir, rot]; + + + +// Function: find_connector() +// Usage: +// find_connector(align, h, size, [size2], [shift], [edges], [corners]); +// Description: +// Generates a list of typical connectors for a cubical region of the given size. +// Arguments: +// align = Named alignment/connector string. +// h = Height of the region. +// size = The [X,Y] size of the bottom of the cubical region. +// size2 = The [X,Y] size of the top of the cubical region. +// shift = The [X,Y] amount to shift the center of the top with respect to the center of the bottom. +// geometry = One of "cube", "cylinder", or "sphere" to denote the overall geometry of the shape. Cones are "cylinder", and prismoids are "cube" for this purpose. Default: "cube" +// extra_conns = A list of extra named connectors. +function find_connector(align, h, size, size2=undef, shift=[0,0], extra_conns=[], geometry="cube") = + is_string(align)? ( + let(found = search([align], extra_conns, num_returns_per_match=1)[0]) + assert(found!=[], str("Unknown alignment: ",align)) + extra_conns[found] + ) : ( + let( + size = point2d(size), + size2 = (size2!=undef)? point2d(size2) : size, + shift = point2d(shift), + oang = ( + align == UP? 0 : + align == DOWN? 0 : + (norm([align.x,align.y]) < EPSILON)? 0 : + atan2(align.y, align.x)+90 + ) + ) + geometry=="sphere"? let( + phi = align==UP? 0 : align==DOWN? 180 : 90 + (45 * align.z), + theta = atan2(align.y, align.x), + vec = spherical_to_xyz(1, theta, phi), + pos = vmul(vec, (point3d(size)+h*UP)/2) + ) [align, pos, vec, oang] : let ( + xyal = ( + geometry=="cylinder"? ( + let(xy = point2d(align)) + norm(xy)>0? xy/norm(xy) : [0,0] + ) : point2d(align) + ), + botpt = point3d(vmul(size/2,xyal))+DOWN*h/2, + toppt = point3d(vmul(size2/2,xyal)+shift)+UP*h/2, + pos = lerp(botpt, toppt, (align.z+1)/2), + sidevec = rotate_points3d([point3d(xyal)], from=UP, to=toppt-botpt)[0], + vec = ( + norm([align.x,align.y]) < EPSILON? align : + abs(align.z) < EPSILON? sidevec : + align.z>0? (UP+sidevec)/2 : + (DOWN+sidevec)/2 + ) + ) [align, pos, vec, oang] + ); + + + +function _str_char_split(s,delim,n=0,acc=[],word="") = + (n>=len(s))? concat(acc, [word]) : + (s[n]==delim)? + _str_char_split(s,delim,n+1,concat(acc,[word]),"") : + _str_char_split(s,delim,n+1,acc,str(word,s[n])); + + + +// Section: Modules + + +// Module: orient_and_align() +// +// Description: +// Takes a vertically oriented shape, and re-orients and aligns it. +// This is useful for making a custom shape available in various +// orientations and alignments without extra translate()s and rotate()s. +// Children should be vertically (Z-axis) oriented, and centered. +// Non-extremity alignment points should be named via the `alignments` arg. +// Named alignments are aligned pre-rotation. +// +// Usage: +// orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments], [chain]) ... +// +// Arguments: +// size = The [X,Y,Z] size of the part. +// size2 = The [X,Y] size of the top of the part. +// shift = The [X,Y] offset of the top of the part, compared to the bottom of the part. +// orient = The axis to align to. Use `ORIENT_` constants from `constants.scad`. +// align = The side of the origin the part should be aligned with. +// center = If given, overrides `align`. If true, centers vertically. If false, `align` will be set to the value in `noncentered`. +// noncentered = The value to set `align` to if `center` == `false`. Default: `BOTTOM`. +// orig_orient = The original orientation of the part. Default: `ORIENT_Z`. +// orig_align = The original alignment of the part. Default: `CENTER`. +// geometry = One of "cube", "cylinder", or "sphere" to denote the overall geometry of the shape. Cones are "cylinder", and prismoids are "cube" for this purpose. Default: "cube" +// alignments = A list of extra, non-standard connectors that can be aligned to. +// chain = If true, allow attachable children. +// +// Side Effects: +// `$parent_size` is set to the parent object's cubical region size. +// `$parent_size2` is set to the parent object's top [X,Y] size. +// `$parent_shift` is set to the parent object's `shift` value, if any. +// `$parent_orient` is set to the parent object's `orient` value. +// `$parent_align` is set to the parent object's `align` value. +// `$parent_geom` is set to the parent object's `geometry` value. +// `$parent_conns` is set to the parent object's list of non-standard extra connectors. +// +// Example: +// #cylinder(d=5, h=10); +// orient_and_align([5,5,10], orient=ORIENT_Y, align=BACK, orig_align=UP) cylinder(d=5, h=10); +module orient_and_align( + size=undef, orient=ORIENT_Z, align=CENTER, + center=undef, noncentered=BOTTOM, + orig_orient=ORIENT_Z, orig_align=CENTER, + size2=undef, shift=[0,0], + alignments=[], chain=false, + geometry="cube" +) { + size2 = point2d(default(size2, size)); + shift = point2d(shift); + align = !is_undef(center)? (center? CENTER : noncentered) : align; + m = matrix4_mult(concat( + (orig_align==CENTER)? [] : [ + // If original alignment is not centered, center it. + matrix4_translate(vmul(size/2, -orig_align)) + ], + (orig_orient==ORIENT_Z)? [] : [ + // If original orientation is not upright, rotate it upright. + matrix4_zrot(-orig_orient.z), + matrix4_yrot(-orig_orient.y), + matrix4_xrot(-orig_orient.x) + ], + ($attach_to!=undef)? ( + let( + conn = find_connector($attach_to, size.z, size, size2=size2, shift=shift, geometry=geometry), + ang = vector_angle(conn[2], DOWN), + axis = vector_axis(conn[2], DOWN), + ang2 = (conn[2]==UP || conn[2]==DOWN)? 0 : 180-conn[3], + axis2 = rotate_points3d([axis],[0,0,ang2])[0] + ) [ + matrix4_translate(-conn[1]), + matrix4_zrot(ang2), + matrix4_rot_by_axis(axis2, ang) + ] + ) : concat( + (align==CENTER)? [] : [ + let(conn = find_connector(align, size.z, size, size2=size2, shift=shift, extra_conns=alignments, geometry=geometry)) + matrix4_translate(-conn[1]) + ], + (orient==ORIENT_Z)? [] : [ + matrix4_xrot(orient.x), + matrix4_yrot(orient.y), + matrix4_zrot(orient.z) + ] + ) + )); + $attach_to = undef; + $parent_size = size; + $parent_size2 = size2; + $parent_shift = shift; + $parent_orient = orient; + $parent_align = align; + $parent_geom = geometry; + $parent_conns = alignments; + tags = _str_char_split($tags, " "); + s_tags = $tags_shown; + h_tags = $tags_hidden; + shown = !s_tags || any([for (tag=tags) in_list(tag, s_tags)]); + hidden = any([for (tag=tags) in_list(tag, h_tags)]); + multmatrix(m) { + if ($children>1 && chain) { + if(shown && !hidden) color($color) for (i=[0:$children-2]) children(i); + children($children-1); + } else { + if(shown && !hidden) color($color) children(); + } + } +} + + + +// Module: attach() +// Usage: +// attach(name, [overlap], [norot]) ... +// attach(name, to, [overlap]) ... +// Description: +// Attaches children to a parent object at an attachment point and orientation. +// Arguments: +// name = The name of the parent attachment point to attach to. +// to = The name of the child attachment point. +// overlap = Amount to sink child into the parent. +// norot = If true, don't rotate children when aligning to the attachment point. +// Example: +// spheroid(d=20) { +// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, align=BOTTOM); +// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5); +// attach(FRONT) down(1.5) cyl(l=11.5, d1=10, d2=5, align=BOTTOM); +// } +module attach(name, to=undef, overlap=undef, norot=false) +{ + assert($parent_size != undef, "No object to attach to!"); + overlap = (overlap!=undef)? overlap : $overlap; + conn = find_connector(name, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, extra_conns=$parent_conns, geometry=$parent_geom); + pos = conn[1]; + vec = conn[2]; + ang = conn[3]; + $attach_to = to; + $attach_conn = conn; + if (norot || (norm(vec-UP)<1e-9 && ang==0)) { + translate(pos) translate([0,0,-overlap]) children(); + } else { + translate(pos) rot(ang,from=UP,to=vec) translate([0,0,-overlap]) children(); + } +} + + +// Module: tags() +// Usage: +// tags(tags) ... +// Description: +// Marks all children with the given tags. +// Arguments: +// tags = String containing space delimited set of tags to apply. +module tags(tags) +{ + $tags = tags; + children(); +} + + +// Module: recolor() +// Usage: +// recolor(c) ... +// Description: +// Sets the color for children that can use the $color special variable. +// Example: +// recolor("red") cyl(l=20, d=10); +module recolor(c) +{ + $color = c; + children(); +} + + +// Module: hide() +// Usage: +// hide(tags) ... +// Description: Hides all children with the given tags. +module hide(tags="") +{ + $tags_hidden = tags==""? [] : _str_char_split(tags, " "); + children(); +} + + +// Module: show() +// Usage: +// show(tags) ... +// Description: Shows only children with the given tags. +module show(tags="") +{ + $tags_shown = tags==""? [] : _str_char_split(tags, " "); + children(); +} + + +// Module: diff() +// Usage: +// diff(neg, [keep]) ... +// diff(neg, pos, [keep]) ... +// Description: +// If `neg` is given, takes the union of all children with tags +// that are in `neg`, and differences them from the union of all +// children with tags in `pos`. If `pos` is not given, then all +// items in `neg` are differenced from all items not in `neg`. If +// `keep` is given, all children with tags in `keep` are then unioned +// with the result. If `keep` is not given, all children without +// tags in `pos` or `neg` are then unioned with the result. +// Arguments: +// neg = String containing space delimited set of tag names of children to difference away. +// pos = String containing space delimited set of tag names of children to be differenced away from. +// keep = String containing space delimited set of tag names of children to keep whole. +module diff(neg, pos=undef, keep=undef) +{ + difference() { + if (pos != undef) { + show(pos) children(); + } else { + if (keep == undef) { + hide(neg) children(); + } else { + hide(str(neg," ",keep)) children(); + } + } + show(neg) children(); + } + if (keep!=undef) { + show(keep) children(); + } else if (pos!=undef) { + hide(str(pos," ",neg)) children(); + } +} + + +// Module: intersect() +// Usage: +// intersect(a, [keep]) ... +// intersect(a, b, [keep]) ... +// Description: +// If `a` is given, takes the union of all children with tags that +// are in `a`, and intersection()s them with the union of all +// children with tags in `b`. If `b` is not given, then the union +// of all items with tags in `a` are intersection()ed with the union +// of all items without tags in `a`. If `keep` is given, then the +// result is unioned with all the children with tags in `keep`. If +// `keep` is not given, all children without tags in `a` or `b` are +// unioned with the result. +// Arguments: +// a = String containing space delimited set of tag names of children. +// b = String containing space delimited set of tag names of children. +// keep = String containing space delimited set of tag names of children to keep whole. +module intersect(a, b=undef, keep=undef) +{ + intersection() { + if (b != undef) { + show(b) children(); + } else { + if (keep == undef) { + hide(a) children(); + } else { + hide(str(a," ",keep)) children(); + } + } + show(a) children(); + } + if (keep!=undef) { + show(keep) children(); + } else if (b!=undef) { + hide(str(a," ",b)) children(); + } +} + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/beziers.scad b/beziers.scad index 014d55f..8f4a303 100644 --- a/beziers.scad +++ b/beziers.scad @@ -885,7 +885,7 @@ function patch_scale(patch, v=[1,1,1], cp=[0,0,0]) = [for(row=patch) scale_point // v = Vector axis to rotate round. // cp = Centerpoint to rotate around. function patch_rotate(patch, a=undef, v=undef, cp=[0,0,0]) = - [for(row=patch) rotate_points3d(row, v=a, axis=v, cp=cp)] : + [for(row=patch) rotate_points3d(row, a=a, v=v, cp=cp)]; // Function: patches_translate() diff --git a/compat.scad b/compat.scad index 9aead82..9e51ed3 100644 --- a/compat.scad +++ b/compat.scad @@ -3,7 +3,7 @@ // Backwards Compatability library // To use, include this line at the top of your file: // ``` -// use +// use // ``` ////////////////////////////////////////////////////////////////////// diff --git a/debug.scad b/debug.scad index 5755fcb..6a6cb31 100644 --- a/debug.scad +++ b/debug.scad @@ -4,8 +4,6 @@ // To use, add the following lines to the beginning of your file: // ``` // include -// include -// include // include // ``` ////////////////////////////////////////////////////////////////////// @@ -173,25 +171,16 @@ module debug_polyhedron(points, faces, convexity=10, txtsize=1, disabled=false) -// Function: all_conns() +// Function: all_connectors() // Description: -// Return the names for all standard connectors for a region. -// Arguments: -// type = The type of region to show connectors for. "cube", "cylinder", "sphere" -function all_conns(type="cube") = - assert(in_list(type,["cube", "cylinder", "sphere"])) - let ( - zs = [TOP, BOTTOM], - ys = [FRONT, BACK], - xs = [LEFT, RIGHT] - ) concat( - [CENTER], - [for (a=concat(xs,ys,zs)) a], - in_list(type,["cube","cylinder"])? [for (a=zs, b=ys) a+b] : [], - in_list(type,["cube","cylinder"])? [for (a=zs, b=xs) a+b] : [], - in_list(type,["cube"])? [for (a=ys, b=xs) a+b] : [], - in_list(type,["cube"])? [for (a=zs, b=ys, c=xs) a+b+c] : [] - ); +// Return the vectors for all standard connectors. +function all_connectors() = [ + for ( + zv = [TOP, CENTER, BOTTOM], + yv = [FRONT, CENTER, BACK], + xv = [LEFT, CENTER, RIGHT] + ) xv+yv+zv +]; @@ -207,11 +196,12 @@ function all_conns(type="cube") = module connector_arrow(s=10, color=[0.333,0.333,1], flag=true) { $fn=12; recolor("gray") spheroid(d=s/6) - recolor(color) cyl(h=s*2/3, d=s/15, align=UP) - attach(TOP) cyl(h=s/3, d1=s/5, d2=0, align=UP) { + recolor(color) cyl(h=s, d=s/15, align=DOWN) + attach(TOP) cyl(h=s/3, d1=s/5, d2=0, align=DOWN) { if(flag) { - attach(BOTTOM) recolor([1,0.5,0.5]) cuboid([s/50, s/6, s/4], align="front-top"); + attach(BOTTOM) recolor([1,0.5,0.5]) cuboid([s/50, s/6, s/4], align=FRONT+TOP); } + children(); } } @@ -219,11 +209,9 @@ module connector_arrow(s=10, color=[0.333,0.333,1], flag=true) { // Module: show_connectors() // Description: -// Show all standard connectors for a given region. -// Arguments: -// type = The type of region to show connectors for. "cube", "cylinder", "sphere" -module show_connectors(type="cube") { - for (conn=all_conns(type)) { +// Show all standard connectors for the parent object. +module show_connectors() { + for (conn=all_connectors()) { attach(conn) connector_arrow(); } children(); @@ -231,16 +219,17 @@ module show_connectors(type="cube") { -// Module: frameref() +// Module: frame_ref() // Description: // Displays X,Y,Z axis arrows in red, green, and blue respectively. // Arguments: // s = Length of the arrows. -module frameref(s=15) { - sphere(0.001) { +module frame_ref(s=15) { + noop() { attach(RIGHT) connector_arrow(s=s, color="red", flag=false); attach(BACK) connector_arrow(s=s, color="green", flag=false); attach(TOP) connector_arrow(s=s, color="blue", flag=false); + children(); } } diff --git a/examples/attachments.scad b/examples/attachments.scad index 8c116de..9309047 100644 --- a/examples/attachments.scad +++ b/examples/attachments.scad @@ -1,17 +1,13 @@ -include -include -include -include -include +include -cuboid([60,40,40], fillet=5, edges=EDGES_Z_ALL, align="bottom") { - attach("top") rounded_prismoid([60,40],[20,20], h=50, r1=5, r2=10) { - attach("top") cylinder(d=20, h=30) { - attach("top") cylinder(d1=50, d2=30, h=12); +cuboid([60,40,40], fillet=5, edges=EDGES_Z_ALL, align=BOTTOM) { + attach(TOP, BOTTOM) rounded_prismoid([60,40],[20,20], h=50, r1=5, r2=10) { + attach(TOP) cylinder(d=20, h=30) { + attach(TOP) cylinder(d1=50, d2=30, h=12); } - for (a = ["front", "back", "left", "right"]) { + for (a = [FRONT, BACK, LEFT, RIGHT]) { attach(a) cylinder(d1=14, d2=5, h=20) { - attach("top", "left", overlap=5) prismoid([30,20], [20,20], h=10, shift=[-7,0]); + attach(TOP, LEFT, overlap=5) prismoid([30,20], [20,20], h=10, shift=[-7,0]); } } } diff --git a/examples/bezier_patches.scad b/examples/bezier_patches.scad index 72266e6..cd4592d 100644 --- a/examples/bezier_patches.scad +++ b/examples/bezier_patches.scad @@ -1,7 +1,6 @@ -include -use -use -use +include +include +include function CR_corner(size, orient=[0,0,0], trans=[0,0,0]) = diff --git a/examples/conical_connectors.scad b/examples/conical_connectors.scad new file mode 100644 index 0000000..7e95466 --- /dev/null +++ b/examples/conical_connectors.scad @@ -0,0 +1,8 @@ +include +include + + +cylinder(h=30, d1=50, d2=30) show_connectors(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/cube_connectors.scad b/examples/cube_connectors.scad new file mode 100644 index 0000000..3eb9cb7 --- /dev/null +++ b/examples/cube_connectors.scad @@ -0,0 +1,8 @@ +include +include + + +cube(40, center=true) show_connectors(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/cylinder_connectors.scad b/examples/cylinder_connectors.scad index 50cc58e..2c05ada 100644 --- a/examples/cylinder_connectors.scad +++ b/examples/cylinder_connectors.scad @@ -1,9 +1,8 @@ -include -include +include include -cylinder(h=30, d1=50, d2=30) show_connectors("cylinder"); +cylinder(h=30, d=30) show_connectors(); // vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/fractal_tree.scad b/examples/fractal_tree.scad index c1aad91..4ab6a58 100644 --- a/examples/fractal_tree.scad +++ b/examples/fractal_tree.scad @@ -1,6 +1,5 @@ -include -include -include +include +include include module leaf(s) { @@ -18,7 +17,7 @@ module leaf(s) { module branches(minsize){ if($parent_size2.x>minsize) { - attach("top") + attach(TOP) zrot(gaussian_rand(90,10)) zring(n=floor(log_rand(2,5,4))) zrot(gaussian_rand(0,5)) @@ -32,7 +31,7 @@ module branches(minsize){ branches(minsize); } else { recolor("springgreen") - attach("top") zrot(90) + attach(TOP) zrot(90) leaf(gaussian_rand(100,5)); } } diff --git a/examples/orientations.scad b/examples/orientations.scad index b1a55a0..fa4318f 100644 --- a/examples/orientations.scad +++ b/examples/orientations.scad @@ -1,6 +1,4 @@ -use -use -include +include // Shows all the orientations on cubes in their correct rotations. diff --git a/examples/prismoid_connectors.scad b/examples/prismoid_connectors.scad index b9104e0..3c5cd74 100644 --- a/examples/prismoid_connectors.scad +++ b/examples/prismoid_connectors.scad @@ -1,5 +1,4 @@ -include -include +include include diff --git a/examples/sphere_connectors.scad b/examples/sphere_connectors.scad new file mode 100644 index 0000000..6d65c70 --- /dev/null +++ b/examples/sphere_connectors.scad @@ -0,0 +1,8 @@ +include +include + + +sphere(d=30) show_connectors(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/tagged_diff.scad b/examples/tagged_diff.scad index 3ebbbbc..23879d6 100644 --- a/examples/tagged_diff.scad +++ b/examples/tagged_diff.scad @@ -1,7 +1,4 @@ -include -include -include -include +include diff("hole", "body pole") diff --git a/primitives.scad b/primitives.scad index e7a3383..08db914 100644 --- a/primitives.scad +++ b/primitives.scad @@ -54,9 +54,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Example: Simple regular cube. // cube(40); -// Example: Rectangular cube, with given X, Y, and Z sizes. +// Example: Rectangular cube. // cuboid([20,40,50]); -module cube(size, center=undef, align=ALLPOS) +// Example: Standard Connectors. +// cube(40, center=true) show_connectors(); +module cube(size, center=undef, align=ALLNEG) { size = scalar_vec3(size); orient_and_align(size, ORIENT_Z, align, center, noncentered=ALLPOS, chain=true) { @@ -96,14 +98,19 @@ module cube(size, center=undef, align=ALLPOS) // cylinder(h=40, d=25); // cylinder(h=40, d1=25, d2=10); // } -module cylinder(r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, l=undef, center=undef, orient=ORIENT_Z, align=UP) +// Example: Standard Connectors +// xdistribute(40) { +// cylinder(h=30, d=25) show_connectors(); +// cylinder(h=30, d1=25, d2=10) show_connectors(); +// } +module cylinder(r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, l=undef, center=undef, orient=ORIENT_Z, align=BOTTOM) { r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); l = first_defined([h, l]); sides = segs(max(r1,r2)); size = [r1*2, r1*2, l]; - orient_and_align(size, orient, align, center, size2=[r2*2,r2*2], noncentered=UP, chain=true) { + orient_and_align(size, orient, align, center, size2=[r2*2,r2*2], noncentered=UP, geometry="cylinder", chain=true) { linear_extrude(height=l, scale=r2/r1, convexity=2, center=true) { circle(r=r1, $fn=sides); } @@ -124,14 +131,18 @@ module cylinder(r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=unde // d = Diameter of the sphere. // orient = Orientation of the sphere, if you don't like where the vertices lay. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`. // align = Alignment of the sphere. Use the constants from `constants.scad`. Default: `CENTER`. -// Example: -// staggered_sphere(d=100); +// Example: By Radius +// sphere(r=50); +// Example: By Diameter +// sphere(d=100); +// Example: Standard Connectors +// sphere(d=50) show_connectors(); module sphere(r=undef, d=undef, orient=ORIENT_Z, align=CENTER) { r = get_radius(r=r, d=d, dflt=1); sides = segs(r); size = [r*2, r*2, r*2]; - orient_and_align(size, orient, align, chain=true) { + orient_and_align(size, orient, align, geometry="sphere", chain=true) { rotate_extrude(convexity=2) { difference() { circle(r=r, $fn=sides); diff --git a/shapes.scad b/shapes.scad index 8d80c6a..383e02f 100644 --- a/shapes.scad +++ b/shapes.scad @@ -76,6 +76,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // cuboid([30,40,50], chamfer=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24); // Example: Rectangular cube with only some edges rounded. // cuboid([30,40,50], fillet=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24); +// Example: Standard Connectors +// cuboid(40, chamfer=5) show_connectors(); module cuboid( size=[1,1,1], p1=undef, p2=undef, @@ -90,11 +92,11 @@ module cuboid( if (!is_undef(p1)) { if (!is_undef(p2)) { translate([for (v=array_zip([p1,p2],0)) min(v)]) { - cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLPOS) children(); + cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLNEG) children(); } } else { translate(p1) { - cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLPOS) children(); + cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLNEG) children(); } } } else { @@ -333,15 +335,17 @@ module upcube(size=[1,1,1]) {siz = scalar_vec3(size); up(siz[2]/2) cube(size=siz // prismoid(size1=[30,60], size2=[0,60], shift=[-15,0], h=30); // Example(FlatSpin): Shifting/Skewing // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]); +// Example(Spin): Standard Connectors +// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) show_connectors(); module prismoid( size1=[1,1], size2=[1,1], h=1, shift=[0,0], - orient=ORIENT_Z, align=UP, center=undef + orient=ORIENT_Z, align=DOWN, center=undef ) { eps = 0.001; shiftby = point3d(point2d(shift)); s1 = [max(size1.x, eps), max(size1.y, eps)]; s2 = [max(size2.x, eps), max(size2.y, eps)]; - orient_and_align([s1.x,s1.y,h], orient, align, center, size2=s2, shift=shift, noncentered=UP, chain=true) { + orient_and_align([s1.x,s1.y,h], orient, align, center, size2=s2, shift=shift, noncentered=DOWN, chain=true) { polyhedron( points=[ [+s2.x/2, +s2.y/2, +h/2] + shiftby, @@ -399,10 +403,12 @@ module prismoid( // rounded_prismoid(size1=[40,60], size2=[40,60], h=20, r1=3, r2=10, $fn=24); // Example(FlatSpin): Shifting/Skewing // rounded_prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5], r=5); +// Example(Spin): Standard Connectors +// rounded_prismoid(size1=[40,60], size2=[40,60], h=20, r1=3, r2=10, $fn=24) show_connectors(); module rounded_prismoid( size1, size2, h, shift=[0,0], r=undef, r1=undef, r2=undef, - align=UP, orient=ORIENT_Z, center=undef + align=DOWN, orient=ORIENT_Z, center=undef ) { eps = 0.001; maxrad1 = min(size1.x/2, size1.y/2); @@ -446,14 +452,16 @@ module rounded_prismoid( // Arguments: // size = [width, thickness, height] // orient = The axis to place the hypotenuse along. Only accepts `ORIENT_X`, `ORIENT_Y`, or `ORIENT_Z` from `constants.scad`. Default: `ORIENT_Y`. -// align = The side of the origin to align to. Use constants from `constants.scad`. Default: `UP+BACK+RIGHT`. -// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=UP+BACK+RIGHT`. +// align = The side of the origin to align to. Use constants from `constants.scad`. Default: `ALLNEG`. +// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=ALLNEG`. // // Example: Centered // right_triangle([60, 10, 40], center=true); // Example: *Non*-Centered // right_triangle([60, 10, 40]); -module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=ALLPOS, center=undef) +// Example: Standard Connectors +// right_triangle([60, 15, 40]) show_connectors(); +module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=ALLNEG, center=undef) { size = scalar_vec3(size); orient_and_align(size, align=align, center=center, chain=true) { @@ -542,7 +550,7 @@ module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=ALLPOS, center=unde // realign = If true, rotate the cylinder by half the angle of one face. // orient = Orientation of the cylinder. Use the `ORIENT_` constants from `constants.scad`. Default: vertical. // align = Alignment of the cylinder. Use the constants from `constants.scad`. Default: centered. -// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=UP`. +// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=DOWN`. // // Example: By Radius // xdistribute(30) { @@ -577,6 +585,13 @@ module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=ALLPOS, center=unde // // Example: Putting it all together // cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, fillet2=5); +// +// Example: Standard Connectors +// xdistribute(40) { +// cyl(l=30, d=25) show_connectors(); +// cyl(l=30, d1=25, d2=10) show_connectors(); +// } +// module cyl( l=undef, h=undef, r=undef, r1=undef, r2=undef, @@ -595,7 +610,7 @@ module cyl( sides = segs(max(r1,r2)); sc = circum? 1/cos(180/sides) : 1; phi = atan2(l, r1-r2); - orient_and_align(size1, orient, align, center=center, size2=size2, chain=true) { + orient_and_align(size1, orient, align, center=center, size2=size2, geometry="cylinder", chain=true) { zrot(realign? 180/sides : 0) { if (!any_defined([chamfer, chamfer1, chamfer2, fillet, fillet1, fillet2])) { cylinder(h=l, r1=r1*sc, r2=r2*sc, center=true, $fn=sides); @@ -722,36 +737,6 @@ module cyl( -// Module: downcyl() -// -// Description: -// Creates a cylinder aligned below the origin. -// -// Usage: -// downcyl(l|h, r|d); -// downcyl(l|h, r1|d1, r2|d2); -// -// Arguments: -// l / h = Length of cylinder. (Default: 1.0) -// r = Radius of cylinder. -// r1 = Bottom radius of cylinder. -// r2 = Top radius of cylinder. -// d = Diameter of cylinder. (use instead of r) -// d1 = Bottom diameter of cylinder. -// d2 = Top diameter of cylinder. -// -// Example: Cylinder -// downcyl(r=20, h=40); -// Example: Cone -// downcyl(r1=10, r2=20, h=40); -module downcyl(r=undef, h=undef, l=undef, d=undef, d1=undef, d2=undef, r1=undef, r2=undef) -{ - l = first_defined([l, h, 1]); - cyl(r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, l=l, align=DOWN) children(); -} - - - // Module: xcyl() // // Description: @@ -770,7 +755,7 @@ module downcyl(r=undef, h=undef, l=undef, d=undef, d1=undef, d2=undef, r1=undef, // d1 = Optional diameter of left (X-) end of cylinder. // d2 = Optional diameter of right (X+) end of cylinder. // align = The side of the origin to align to. Use constants from `constants.scad`. Default: `CENTER` -// center = If given, overrides `align`. A `true` value sets `align=CENTER`, `false` sets `align=UP`. +// center = If given, overrides `align`. A `true` value sets `align=CENTER`, `false` sets `align=BOTTOM`. // // Example: By Radius // ydistribute(50) { @@ -909,6 +894,8 @@ module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h // tube(h=30, or1=40, or2=25, ir1=35, ir2=20); // Example: Circular Wedge // tube(h=30, or1=40, or2=30, ir1=20, ir2=30); +// Example: Standard Connectors +// tube(h=30, or=40, wall=5) show_connectors(); module tube( h=1, wall=undef, r=undef, r1=undef, r2=undef, @@ -929,7 +916,7 @@ module tube( sides = segs(max(r1,r2)); size = [r1*2,r1*2,h]; size2 = [r2*2,r2*2,h]; - orient_and_align(size, orient, align, center=center, size2=size2, chain=true) { + orient_and_align(size, orient, align, center=center, size2=size2, geometry="cylinder", chain=true) { zrot(realign? 180/sides : 0) { difference() { cyl(h=h, r1=r1, r2=r2, $fn=sides) children(); @@ -968,6 +955,8 @@ module tube( // torus(d=45, d2=15); // torus(or=30, ir=15); // torus(od=60, id=30); +// Example: Standard Connectors +// torus(od=60, id=30) show_connectors(); module torus( r=undef, d=undef, r2=undef, d2=undef, @@ -980,7 +969,7 @@ module torus( majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2); minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2); size = [(majrad+minrad)*2, (majrad+minrad)*2, minrad*2]; - orient_and_align(size, orient, align, center=center, chain=true) { + orient_and_align(size, orient, align, center=center, geometry="cylinder", chain=true) { rotate_extrude(convexity=4) { right(majrad) circle(minrad); } @@ -1004,8 +993,12 @@ module torus( // circum = If true, circumscribes the perfect sphere of the given radius/diameter. // orient = Orientation of the sphere, if you don't like where the vertices lay. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`. // align = Alignment of the sphere. Use the constants from `constants.scad`. Default: `CENTER`. -// Example: -// spheroid(d=100, circum=true, $fn=10); +// Example: By Radius +// spheroid(r=50, circum=true); +// Example: By Diameter +// spheroid(d=100, circum=true); +// Example: Standard Connectors +// spheroid(d=40, circum=true) show_connectors(); module spheroid(r=undef, d=undef, circum=false, orient=UP, align=CENTER) { r = get_radius(r=r, d=d, dflt=1); @@ -1013,7 +1006,7 @@ module spheroid(r=undef, d=undef, circum=false, orient=UP, align=CENTER) vsides = ceil(hsides/2); rr = circum? (r / cos(90/vsides) / cos(180/hsides)) : r; size = [2*rr, 2*rr, 2*rr]; - orient_and_align(size, orient, align, chain=true) { + orient_and_align(size, orient, align, geometry="sphere", chain=true) { sphere(r=rr); children(); } @@ -1036,8 +1029,12 @@ module spheroid(r=undef, d=undef, circum=false, orient=UP, align=CENTER) // orient = Orientation of the sphere, if you don't like where the vertices lay. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`. // align = Alignment of the sphere. Use the constants from `constants.scad`. Default: `CENTER`. // -// Example: -// staggered_sphere(d=100, circum=true, $fn=10); +// Example: By Radius +// staggered_sphere(r=50, circum=true); +// Example: By Diameter +// staggered_sphere(d=100, circum=true); +// Example: Standard Connectors +// staggered_sphere(d=40, circum=true) show_connectors(); module staggered_sphere(r=undef, d=undef, circum=false, orient=UP, align=CENTER) { r = get_radius(r=r, d=d, dflt=1); sides = segs(r); @@ -1075,7 +1072,7 @@ module staggered_sphere(r=undef, d=undef, circum=false, orient=UP, align=CENTER) ] ); size = [2*rr, 2*rr, 2*rr]; - orient_and_align(size, orient, align, chain=true) { + orient_and_align(size, orient, align, geometry="sphere", chain=true) { polyhedron(points=pts, faces=faces); children(); } @@ -1153,7 +1150,7 @@ module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, orient= r = get_radius(r=r, d=d, dflt=1); l = first_defined([l, h, 1]); size = [r*2,r*2,l]; - orient_and_align(size, orient, align, chain=true) { + orient_and_align(size, orient, align, geometry="cylinder", chain=true) { linear_extrude(height=l, center=true, slices=2) { teardrop2d(r=r, ang=ang, cap_h=cap_h); } @@ -1184,13 +1181,18 @@ module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, orient= // onion(r=30, maxang=30, cap_h=40); // Example: Close Crop // onion(r=30, maxang=30, cap_h=20); +// Example: Standard Connectors +// onion(r=30, maxang=30, cap_h=40) show_connectors(); module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, orient=ORIENT_Z, align=CENTER) { r = get_radius(r=r, d=d, dflt=1); h = first_defined([cap_h, h]); maxd = 3*r/tan(maxang); size = [r*2,r*2,r*2]; - orient_and_align(size, orient, align, chain=true) { + alignments = [ + ["cap", [0,0,h], UP, 0] + ]; + orient_and_align(size, orient, align, geometry="sphere", alignments=alignments, chain=true) { rotate_extrude(convexity=2) { difference() { teardrop2d(r=r, ang=maxang, cap_h=h); @@ -1724,7 +1726,7 @@ module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, a // // Description: // Useful when you MUST pass a child to a module, but you want it to be nothing. -module nil() union() {} +module nil() union(){} // Module: noop() @@ -1732,7 +1734,7 @@ module nil() union() {} // Description: // Passes through the children passed to it, with no action at all. // Useful while debugging when you want to replace a command. -module noop() children(); +module noop(orient=ORIENT_Z) orient_and_align([0,0,0], orient, CENTER, chain=true) {nil(); children();} // Module: pie_slice() @@ -1773,7 +1775,7 @@ module pie_slice( r2 = get_radius(r2, r, d2, d, 10); maxd = max(r1,r2)+0.1; size = [2*r1, 2*r1, l]; - orient_and_align(size, orient, align, center=center, chain=true) { + orient_and_align(size, orient, align, center=center, geometry="cylinder", chain=true) { difference() { cylinder(r1=r1, r2=r2, h=l, center=true); if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true); @@ -1919,7 +1921,7 @@ module arced_slot( fn_minor = first_defined([$fn2, $fn]); da = ea - sa; size = [r+sr1, r+sr1, h]; - orient_and_align(size, orient, align, chain=true) { + orient_and_align(size, orient, align, geometry="cylinder", chain=true) { translate(cp) { zrot(sa) { difference() { diff --git a/std.scad b/std.scad index 79f22e7..555f40c 100644 --- a/std.scad +++ b/std.scad @@ -36,18 +36,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -include -include -include -include -include -include -include -include -include -include -include -include +include +include +include +include +include +include +include +include +include +include +include +include +include // vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/transforms.scad b/transforms.scad index 1eeae4e..eab6b5d 100644 --- a/transforms.scad +++ b/transforms.scad @@ -2173,330 +2173,4 @@ module shell2d(thickness, or=0, ir=0, fill=0, round=0) -////////////////////////////////////////////////////////////////////// -// Section: Miscellaneous -////////////////////////////////////////////////////////////////////// - - -// Module: orient_and_align() -// -// Description: -// Takes a vertically oriented shape, and re-orients and aligns it. -// This is useful for making a custom shape available in various -// orientations and alignments without extra translate()s and rotate()s. -// Children should be vertically (Z-axis) oriented, and centered. -// Non-extremity alignment points should be named via the `alignments` arg. -// Named alignments are aligned pre-rotation. -// -// Usage: -// orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments], [chain]) ... -// -// Arguments: -// size = The [X,Y,Z] size of the part. -// size2 = The [X,Y] size of the top of the part. -// shift = The [X,Y] offset of the top of the part, compared to the bottom of the part. -// orient = The axis to align to. Use `ORIENT_` constants from `constants.scad`. -// align = The side of the origin the part should be aligned with. -// center = If given, overrides `align`. If true, centers vertically. If false, `align` will be set to the value in `noncentered`. -// noncentered = The value to set `align` to if `center` == `false`. Default: `UP`. -// orig_orient = The original orientation of the part. Default: `ORIENT_Z`. -// orig_align = The original alignment of the part. Default: `CENTER`. -// alignments = A list of extra, non-standard connectors that can be aligned to. -// chain = If true, allow attachable children. -// -// Side Effects: -// `$parent_size` is set to the parent object's cubical region size. -// `$parent_size2` is set to the parent object's top [X,Y] size. -// `$parent_shift` is set to the parent object's `shift` value, if any. -// `$parent_orient` is set to the parent object's `orient` value. -// `$parent_align` is set to the parent object's `align` value. -// `$parent_conns` is set to the parent object's list of non-standard extra connectors. -// -// Example: -// #cylinder(d=5, h=10); -// orient_and_align([5,5,10], orient=ORIENT_Y, align=BACK, orig_align=UP) cylinder(d=5, h=10); -module orient_and_align( - size=undef, orient=ORIENT_Z, align=CENTER, - center=undef, noncentered=TOP, - orig_orient=ORIENT_Z, orig_align=CENTER, - size2=undef, shift=[0,0], - alignments=[], chain=false -) { - size2 = point2d(default(size2, size)); - shift = point2d(shift); - align = !is_undef(center)? (center? CENTER : noncentered) : align; - m = matrix4_mult(concat( - (orig_align==CENTER)? [] : [ - // If original alignment is not centered, center it. - matrix4_translate(vmul(size/2, -orig_align)) - ], - (orig_orient==ORIENT_Z)? [] : [ - // If original orientation is not upright, rotate it upright. - matrix4_zrot(-orig_orient.z), - matrix4_yrot(-orig_orient.y), - matrix4_xrot(-orig_orient.x) - ], - ($attach_to!=undef)? ( - let( - conn = find_connector($attach_to, size.z, size, size2=size2, shift=shift), - ang = vector_angle(conn[2], DOWN), - axis = vector_axis(conn[2], DOWN), - ang2 = (conn[2]==UP || conn[2]==DOWN)? 0 : 180-conn[3], - axis2 = rotate_points3d([axis],[0,0,ang2])[0] - ) [ - matrix4_translate(-conn[1]), - matrix4_zrot(ang2), - matrix4_rot_by_axis(axis2, ang) - ] - ) : concat( - (align==CENTER)? [] : [ - let(conn = find_connector(align, size.z, size, size2=size2, shift=shift, extra_conns=alignments)) - matrix4_translate(-conn[1]) - ], - (orient==ORIENT_Z)? [] : [ - matrix4_xrot(orient.x), - matrix4_yrot(orient.y), - matrix4_zrot(orient.z) - ] - ) - )); - $attach_to = undef; - $parent_size = size; - $parent_size2 = size2; - $parent_shift = shift; - $parent_orient = orient; - $parent_align = align; - $parent_conns = alignments; - tags = _str_char_split($tags, " "); - s_tags = $tags_shown; - h_tags = $tags_hidden; - shown = !s_tags || any([for (tag=tags) in_list(tag, s_tags)]); - hidden = any([for (tag=tags) in_list(tag, h_tags)]); - echo(tags=tags, shown=shown, hidden=hidden, view=shown&&!hidden); - multmatrix(m) { - if ($children>1 && chain) { - if(shown && !hidden) color($color) for (i=[0:$children-2]) children(i); - children($children-1); - } else { - if(shown && !hidden) color($color) children(); - } - } -} - - - -// Function: connector() -// Usage: -// connector(name, pos, dir, [rot]) -// Description: -// Creates a connector data structure. -// Arguments: -// name = The string name of the connector. Lowercase. Words separated by single dashes. No spaces. -// pos = The [X,Y,Z] position of the connector. -// dir = A vector pointing in the direction parts should project from the connector position. -// rot = If needed, the angle to rotate the part around the direction vector. -function connector(name, pos=[0,0,0], dir=UP, rot=0) = [name, pos, dir, rot]; - - - -// Function: find_connector() -// Usage: -// find_connector(align, h, size, [size2], [shift], [edges], [corners]); -// Description: -// Generates a list of typical connectors for a cubical region of the given size. -// Arguments: -// align = Named alignment/connector string. -// h = Height of the region. -// size = The [X,Y] size of the bottom of the cubical region. -// size2 = The [X,Y] size of the top of the cubical region. -// shift = The [X,Y] amount to shift the center of the top with respect to the center of the bottom. -// extra_conns = A list of extra named connectors. -function find_connector(align, h, size, size2=undef, shift=[0,0], extra_conns=[]) = - let( - shift = point3d(shift), - size = point3d(point2d(size)), - size2 = (size2!=undef)? point3d(point2d(size2)) : size, - found = !is_string(align)? [] : search([align], extra_conns, num_returns_per_match=1)[0] - ) (found!=[])? extra_conns[found] : let( - top = [-size2/2+shift, shift, size2/2+shift], - bot = [-size/2, CENTER, size/2], - toppt = [top[align.x+1].x, top[align.y+1].y, h/2], - botpt = [bot[align.x+1].x, bot[align.y+1].y, -h/2], - pos = lerp(botpt, toppt, (align.z+1)/2), - oang = ( - align == UP? 0 : - align == DOWN? 0 : - (norm([align.x,align.y]) < EPSILON)? 0 : - atan2(align.y, align.x)+90 - ), - vec = ( - abs(align.z) > EPSILON? align : - rotate_points3d([align], from=UP, to=toppt-botpt)[0] - ) - ) [align, pos, vec, oang]; - - - -// Module: attach() -// Usage: -// attach(name, [overlap], [norot]) ... -// attach(name, to, [overlap]) ... -// Description: -// Attaches children to a parent object at an attachment point and orientation. -// Arguments: -// name = The name of the parent attachment point to attach to. -// to = The name of the child attachment point. -// overlap = Amount to sink child into the parent. -// norot = If true, don't rotate children when aligning to the attachment point. -// Example: -// spheroid(d=20) { -// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, align=BOTTOM); -// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5); -// attach(FRONT) down(1.5) cyl(l=11.5, d1=10, d2=5, align=BOTTOM); -// } -module attach(name, to=undef, overlap=undef, norot=false) -{ - assert($parent_size != undef, "No object to attach to!"); - overlap = (overlap!=undef)? overlap : $overlap; - conn = find_connector(name, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, extra_conns=$parent_conns); - pos = conn[1]; - vec = conn[2]; - ang = conn[3]; - $attach_to = to; - $attach_conn = conn; - if (norot || (norm(vec-UP)<1e-9 && ang==0)) { - translate(pos) translate([0,0,-overlap]) children(); - } else { - translate(pos) rot(ang,from=UP,to=vec) translate([0,0,-overlap]) children(); - } -} - - -// Module: tags() -// Usage: -// tags(tags) ... -// Description: -// Marks all children with the given tags. -// Arguments: -// tags = String containing space delimited set of tags to apply. -module tags(tags) -{ - $tags = tags; - children(); -} - - -// Module: recolor() -// Usage: -// recolor(c) ... -// Description: -// Sets the color for children that can use the $color special variable. -// Example: -// recolor("red") cyl(l=20, d=10); -module recolor(c) -{ - $color = c; - children(); -} - - -// Module: hide() -// Usage: -// hide(tags) ... -// Description: Hides all children with the given tags. -module hide(tags="") -{ - $tags_hidden = tags==""? [] : _str_char_split(tags, " "); - children(); -} - - -// Module: show() -// Usage: -// show(tags) ... -// Description: Shows only children with the given tags. -module show(tags="") -{ - $tags_shown = tags==""? [] : _str_char_split(tags, " "); - children(); -} - - -// Module: diff() -// Usage: -// diff(neg, [keep]) ... -// diff(neg, pos, [keep]) ... -// Description: -// If `neg` is given, takes the union of all children with tags -// that are in `neg`, and differences them from the union of all -// children with tags in `pos`. If `pos` is not given, then all -// items in `neg` are differenced from all items not in `neg`. If -// `keep` is given, all children with tags in `keep` are then unioned -// with the result. If `keep` is not given, all children without -// tags in `pos` or `neg` are then unioned with the result. -// Arguments: -// neg = String containing space delimited set of tag names of children to difference away. -// pos = String containing space delimited set of tag names of children to be differenced away from. -// keep = String containing space delimited set of tag names of children to keep whole. -module diff(neg, pos=undef, keep=undef) -{ - difference() { - if (pos != undef) { - show(pos) children(); - } else { - if (keep == undef) { - hide(neg) children(); - } else { - hide(str(neg," ",keep)) children(); - } - } - show(neg) children(); - } - if (keep!=undef) { - show(keep) children(); - } else if (pos!=undef) { - hide(str(pos," ",neg)) children(); - } -} - - -// Module: intersect() -// Usage: -// intersect(a, [keep]) ... -// intersect(a, b, [keep]) ... -// Description: -// If `a` is given, takes the union of all children with tags that -// are in `a`, and intersection()s them with the union of all -// children with tags in `b`. If `b` is not given, then the union -// of all items with tags in `a` are intersection()ed with the union -// of all items without tags in `a`. If `keep` is given, then the -// result is unioned with all the children with tags in `keep`. If -// `keep` is not given, all children without tags in `a` or `b` are -// unioned with the result. -// Arguments: -// a = String containing space delimited set of tag names of children. -// b = String containing space delimited set of tag names of children. -// keep = String containing space delimited set of tag names of children to keep whole. -module intersect(a, b=undef, keep=undef) -{ - intersection() { - if (b != undef) { - show(b) children(); - } else { - if (keep == undef) { - hide(a) children(); - } else { - hide(str(a," ",keep)) children(); - } - } - show(a) children(); - } - if (keep!=undef) { - show(keep) children(); - } else if (b!=undef) { - hide(str(a," ",b)) children(); - } -} - - // vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap