Fixed corner and top/bottom edge orientations.

This commit is contained in:
Revar Desmera 2019-04-22 01:08:41 -07:00
parent cc36235736
commit 7e1598d66d
18 changed files with 550 additions and 465 deletions

397
attachments.scad Normal file
View file

@ -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 <BOSL2/std.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
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

View file

@ -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()

View file

@ -3,7 +3,7 @@
// Backwards Compatability library
// To use, include this line at the top of your file:
// ```
// use <compat.scad>
// use <BOSL2/std.scad>
// ```
//////////////////////////////////////////////////////////////////////

View file

@ -4,8 +4,6 @@
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL2/std.scad>
// include <BOSL2/paths.scad>
// include <BOSL2/beziers.scad>
// include <BOSL2/debug.scad>
// ```
//////////////////////////////////////////////////////////////////////
@ -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();
}
}

View file

@ -1,17 +1,13 @@
include <BOSL2/constants.scad>
include <BOSL2/transforms.scad>
include <BOSL2/primitives.scad>
include <BOSL2/shapes.scad>
include <BOSL2/debug.scad>
include <BOSL2/std.scad>
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]);
}
}
}

View file

@ -1,7 +1,6 @@
include <BOSL2/constants.scad>
use <BOSL2/transforms.scad>
use <BOSL2/beziers.scad>
use <BOSL2/math.scad>
include <BOSL2/std.scad>
include <BOSL2/paths.scad>
include <BOSL2/beziers.scad>
function CR_corner(size, orient=[0,0,0], trans=[0,0,0]) =

View file

@ -0,0 +1,8 @@
include <BOSL2/std.scad>
include <BOSL2/debug.scad>
cylinder(h=30, d1=50, d2=30) show_connectors();
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -0,0 +1,8 @@
include <BOSL2/std.scad>
include <BOSL2/debug.scad>
cube(40, center=true) show_connectors();
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,9 +1,8 @@
include <BOSL2/constants.scad>
include <BOSL2/primitives.scad>
include <BOSL2/std.scad>
include <BOSL2/debug.scad>
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

View file

@ -1,6 +1,5 @@
include <BOSL2/constants.scad>
include <BOSL2/transforms.scad>
include <BOSL2/primitives.scad>
include <BOSL2/std.scad>
include <BOSL2/paths.scad>
include <BOSL2/beziers.scad>
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));
}
}

View file

@ -1,6 +1,4 @@
use <BOSL2/transforms.scad>
use <BOSL2/math.scad>
include <BOSL2/constants.scad>
include <BOSL2/std.scad>
// Shows all the orientations on cubes in their correct rotations.

View file

@ -1,5 +1,4 @@
include <BOSL2/constants.scad>
include <BOSL2/shapes.scad>
include <BOSL2/std.scad>
include <BOSL2/debug.scad>

View file

@ -0,0 +1,8 @@
include <BOSL2/std.scad>
include <BOSL2/debug.scad>
sphere(d=30) show_connectors();
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,7 +1,4 @@
include <BOSL2/constants.scad>
include <BOSL2/transforms.scad>
include <BOSL2/primitives.scad>
include <BOSL2/shapes.scad>
include <BOSL2/std.scad>
diff("hole", "body pole")

View file

@ -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);

View file

@ -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);
@ -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() {

View file

@ -36,18 +36,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <BOSL2/constants.scad>
include <BOSL2/compat.scad>
include <BOSL2/math.scad>
include <BOSL2/arrays.scad>
include <BOSL2/vector.scad>
include <BOSL2/matrices.scad>
include <BOSL2/coords.scad>
include <BOSL2/geometry.scad>
include <BOSL2/transforms.scad>
include <BOSL2/primitives.scad>
include <BOSL2/shapes.scad>
include <BOSL2/masks.scad>
include <constants.scad>
include <compat.scad>
include <math.scad>
include <arrays.scad>
include <vectors.scad>
include <matrices.scad>
include <coords.scad>
include <geometry.scad>
include <attachments.scad>
include <transforms.scad>
include <primitives.scad>
include <shapes.scad>
include <masks.scad>
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -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