mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
708 lines
27 KiB
OpenSCAD
708 lines
27 KiB
OpenSCAD
//////////////////////////////////////////////////////////////////////
|
|
// 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>
|
|
// ```
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Default values for attachment code.
|
|
$tags = "";
|
|
$overlap = 0.01;
|
|
$color = undef;
|
|
|
|
$attach_to = undef;
|
|
$attach_anchor = [CENTER, CENTER, UP, 0];
|
|
$attach_norot = false;
|
|
|
|
$parent_size = undef;
|
|
$parent_size2 = undef;
|
|
$parent_shift = [0,0];
|
|
$parent_anchors = [];
|
|
$parent_anchor = BOTTOM;
|
|
$parent_orient = UP;
|
|
|
|
$tags_shown = [];
|
|
$tags_hidden = [];
|
|
|
|
|
|
// Section: Anchors, Spin, and Orientation
|
|
// This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()`
|
|
// and `sphere()` builtins, as well as to most of the shapes provided by this library itself.
|
|
// * An anchor is a place on an object which you can align the object to, or attach other objects
|
|
// to using `attach()` or `position()`. An anchor has a position, a direction, and a spin.
|
|
// The direction and spin are used to orient other objects to match when using `attach()`.
|
|
// * Spin is a simple rotation around the Z axis.
|
|
// * Orientation is rotating an object so that its top is pointed towards a given vector.
|
|
// An object will first be translated to its anchor position, then spun, then oriented.
|
|
//
|
|
// ## Anchor
|
|
// Anchoring is specified with the `anchor` argument in most shape modules.
|
|
// Specifying `anchor` when creating an object will translate the object so
|
|
// that the anchor point is at the origin (0,0,0). Anchoring always occurs
|
|
// before spin and orientation are applied.
|
|
//
|
|
// An anchor can be referred to in one of two ways; as a directional vector,
|
|
// or as a named anchor string.
|
|
//
|
|
// When given as a vector, it points, in a general way, towards the face, edge, or
|
|
// corner of the object that you want the anchor for, relative to the center of
|
|
// the object. There are directional constants named `TOP`, `BOTTOM`, `FRONT`, `BACK`,
|
|
// `LEFT`, and `RIGHT` that you can add together to specify an anchor point.
|
|
// For example:
|
|
// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face.
|
|
// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge.
|
|
// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner.
|
|
//
|
|
// The components of the directional vector should all be `1`, `0`, or `-1`.
|
|
// When the object is cylindrical, conical, or spherical in nature, the anchors will be
|
|
// located around the surface of the cylinder, cone, or sphere, relative to the center.
|
|
// The direction of a face anchor will be perpendicular to the face, pointing outward.
|
|
// The direction of a edge anchor will be the average of the anchor directions of the
|
|
// two faces the edge is between. The direction of a corner anchor will be the average
|
|
// of the anchor directions of the three faces the corner is on. The spin of all standard
|
|
// anchors is 0.
|
|
//
|
|
// Some more complex objects, like screws and stepper motors, have named anchors
|
|
// to refer to places on the object that are not at one of the standard faces, edges
|
|
// or corners. For example, stepper motors have anchors for `"screw1"`, `"screw2"`,
|
|
// etc. to refer to the various screwholes on the stepper motor shape. The names,
|
|
// positions, directions, and spins of these anchors will be specific to the object,
|
|
// and will be documented when they exist.
|
|
//
|
|
// ## Spin
|
|
// Spin is specified with the `spin` argument in most shape modules. Specifying `spin`
|
|
// when creating an object will rotate the object counter-clockwise around the Z axis
|
|
// by the given number of degrees. Spin is always applied after anchoring, and before
|
|
// orientation.
|
|
//
|
|
// ## Orient
|
|
// Orientation is specified with the `orient` argument in most shape modules. Specifying
|
|
// `orient` when creating an object will rotate the object such that the top of the
|
|
// object will be pointed at the vector direction given in the `orient` argument.
|
|
// Orientation is always applied after anchoring and spin. The constants `UP`, `DOWN`,
|
|
// `FRONT`, `BACK`, `LEFT`, and `RIGHT` can be added together to form the directional
|
|
// vector for this. ie: `LEFT+BACK`
|
|
|
|
|
|
// Section: Functions
|
|
|
|
|
|
// Function: anchorpt()
|
|
// Usage:
|
|
// anchor(name, pos, [dir], [rot])
|
|
// Description:
|
|
// Creates a anchor data structure.
|
|
// Arguments:
|
|
// name = The string name of the anchor. Lowercase. Words separated by single dashes. No spaces.
|
|
// pos = The [X,Y,Z] position of the anchor.
|
|
// orient = A vector pointing in the direction parts should project from the anchor position.
|
|
// spin = If needed, the angle to rotate the part around the direction vector.
|
|
function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, spin];
|
|
|
|
|
|
|
|
// Function: find_anchor()
|
|
// Usage:
|
|
// find_anchor(anchor, h, size, [size2], [shift], [edges], [corners]);
|
|
// Description:
|
|
// Returns anchor data for the given vector or anchor name.
|
|
// Arguments:
|
|
// anchor = Vector or named anchor 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.
|
|
// offset = If the anchor is not CENTER, this is the offset to add to the rest of the anchor points.
|
|
// 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"
|
|
// anchors = A list of extra non-standard named anchors.
|
|
// two_d = If true, object will be treated as 2D.
|
|
function find_anchor(anchor, h, size, size2=undef, shift=[0,0], offset=[0,0,0], anchors=[], geometry="cube", two_d=false) =
|
|
is_string(anchor)? (
|
|
let(found = search([anchor], anchors, num_returns_per_match=1)[0])
|
|
assert(found!=[], str("Unknown anchor: ",anchor))
|
|
anchors[found]
|
|
) : (
|
|
assert(is_vector(anchor),str("anchor=",anchor))
|
|
let(
|
|
size = point2d(size),
|
|
size2 = (size2!=undef)? point2d(size2) : size,
|
|
shift = point2d(shift),
|
|
oang = (
|
|
two_d? 0 :
|
|
anchor == UP? 0 :
|
|
anchor == DOWN? 0 :
|
|
(norm([anchor.x,anchor.y]) < EPSILON)? 0 :
|
|
atan2(anchor.y, anchor.x)+90
|
|
)
|
|
)
|
|
geometry=="sphere"? let(
|
|
phi = (anchor==UP||anchor==CENTER)? 0 : anchor==DOWN? 180 : 90 + (45 * anchor.z),
|
|
theta = anchor==CENTER? 90 : atan2(anchor.y, anchor.x),
|
|
vec = spherical_to_xyz(1, theta, phi),
|
|
offset = anchor==CENTER? [0,0,0] : offset,
|
|
pos = anchor==CENTER? CENTER : vmul(vec, (point3d(size)+h*UP)/2) + offset
|
|
) [anchor, pos, vec, oang] : let (
|
|
xyal = (
|
|
geometry=="cylinder"? (
|
|
let(xy = point2d(anchor))
|
|
norm(xy)>0? xy/norm(xy) : [0,0]
|
|
) : point2d(anchor)
|
|
),
|
|
botpt = point3d(vmul(size/2,xyal))+DOWN*h/2,
|
|
toppt = point3d(vmul(size2/2,xyal)+shift)+UP*h/2,
|
|
offset = anchor==CENTER? [0,0,0] : offset,
|
|
pos = lerp(botpt, toppt, (anchor.z+1)/2) + offset,
|
|
sidevec = two_d? point3d(xyal) :
|
|
approx(norm(xyal),0)? [0,0,0] :
|
|
rotate_points3d([point3d(xyal)], from=UP, to=toppt-botpt)[0],
|
|
vec = (
|
|
two_d? sidevec :
|
|
anchor==CENTER? UP :
|
|
norm([anchor.x,anchor.y]) < EPSILON? anchor :
|
|
norm(size)+norm(size2) < EPSILON? anchor :
|
|
abs(anchor.z) < EPSILON? sidevec :
|
|
anchor.z>0? (UP+sidevec)/2 :
|
|
(DOWN+sidevec)/2
|
|
)
|
|
) [anchor, 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_anchor()
|
|
//
|
|
// Description:
|
|
// Takes a vertically oriented part and anchors, spins and orients it.
|
|
// This is useful for making a custom shape available in various
|
|
// orientations and anchorings without extra translate()s and rotate()s.
|
|
// Children should be vertically (Z-axis) oriented, and centered.
|
|
// Non-vector anchor points should be named via the `anchors` arg.
|
|
//
|
|
// If this is *not* run as a child of `attach()` with the `to` argument
|
|
// given, then the following transformations are performed in order:
|
|
// * Translates so the `anchor` point is at the origin (0,0,0).
|
|
// * Rotates around the Z axis by `spin` degrees counter-clockwise.
|
|
// * Rotates so the top of the part points towards the vector `orient`.
|
|
//
|
|
// If this is called as a child of `attach(from,to)`, then the info
|
|
// for the anchor points referred to by `from` and `to` are fetched,
|
|
// which will include position, direction, and spin. With that info,
|
|
// the following transformations are performed:
|
|
// * Translates this part so it's anchor position matches the parent's anchor position.
|
|
// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector.
|
|
// * Rotates this part so it's anchor spin matches the parent's anchor spin.
|
|
//
|
|
// Usage:
|
|
// orient_and_anchor(size, [anchor], [spin], [orient], [center], [noncentered], [anchors], [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.
|
|
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
|
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
|
// center = If given, overrides `anchor`. If true, centers vertically. If false, `anchor` will be set to the value in `noncentered`.
|
|
// noncentered = The value to set `anchor` to if `center` == `false`. Default: `BOTTOM`.
|
|
// offset = The offset of the center of the object from the CENTER anchor.
|
|
// 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"
|
|
// anchors = A list of extra, non-standard optional anchors.
|
|
// chain = If true, allow attachable children.
|
|
// two_d = If true, object will be treated as 2D.
|
|
//
|
|
// 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_geom` is set to the parent object's `geometry` value.
|
|
// `$parent_orient` is set to the parent object's `orient` value.
|
|
// `$parent_anchor` is set to the parent object's `anchor` value.
|
|
// `$parent_anchors` is set to the parent object's list of non-standard extra anchors.
|
|
// `$parent_2d` is set to the parent object's `two_d` value.
|
|
//
|
|
// Example(Med):
|
|
// #cylinder(d1=50, d2=30, h=60);
|
|
// orient_and_anchor(size=[50,50,60], size2=[30,30], anchor=RIGHT, orient=FWD)
|
|
// cylinder(d1=50, d2=30, h=60);
|
|
module orient_and_anchor(
|
|
size=undef,
|
|
orient=UP,
|
|
anchor=CENTER,
|
|
center=undef,
|
|
noncentered=BOTTOM,
|
|
spin=0,
|
|
size2=undef,
|
|
shift=[0,0],
|
|
offset=[0,0,0],
|
|
geometry="cube",
|
|
anchors=[],
|
|
chain=false,
|
|
two_d=false
|
|
) {
|
|
size2 = point2d(default(size2, size));
|
|
shift = point2d(shift);
|
|
anchr = is_undef(center)? anchor : (center? CENTER : noncentered);
|
|
pos = find_anchor(anchr, size.z, size, size2=size2, shift=shift, offset=offset, anchors=anchors, geometry=geometry, two_d=two_d)[1];
|
|
|
|
$parent_size = size;
|
|
$parent_size2 = size2;
|
|
$parent_shift = shift;
|
|
$parent_geom = geometry;
|
|
$parent_orient = orient;
|
|
$parent_offset = offset;
|
|
$parent_2d = two_d;
|
|
$parent_anchor = anchr;
|
|
$parent_anchors = anchors;
|
|
|
|
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)]);
|
|
if ($attach_to != undef) {
|
|
anch = find_anchor($attach_to, size.z, size, size2=size2, shift=shift, offset=offset, anchors=anchors, geometry=geometry, two_d=two_d);
|
|
ang = vector_angle(anch[2], two_d? BACK : DOWN);
|
|
axis = two_d? UP : vector_axis(anch[2], DOWN);
|
|
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3];
|
|
axis2 = rotate_points3d([axis],[0,0,ang2])[0];
|
|
$attach_to = undef;
|
|
|
|
rot(ang, v=axis2)
|
|
rotate(ang2+spin)
|
|
translate(-anch[1])
|
|
{
|
|
if ($children>1 && chain) {
|
|
if(shown && !hidden) {
|
|
color($color) for (i=[0:1:$children-2]) children(i);
|
|
}
|
|
children($children-1);
|
|
} else {
|
|
if(shown && !hidden) color($color) children();
|
|
}
|
|
}
|
|
} else {
|
|
rot(from=UP,to=orient)
|
|
rotate(spin)
|
|
translate(-pos)
|
|
{
|
|
if ($children>1 && chain) {
|
|
if(shown && !hidden) {
|
|
color($color) for (i=[0:1:$children-2]) children(i);
|
|
}
|
|
children($children-1);
|
|
} else {
|
|
if(shown && !hidden) color($color) children();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: position()
|
|
// Usage:
|
|
// position(from, [overlap]) ...
|
|
// Description:
|
|
// Attaches children to a parent object at an anchor point.
|
|
// Arguments:
|
|
// from = The vector, or name of the parent anchor point to attach to.
|
|
// Example:
|
|
// spheroid(d=20) {
|
|
// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
|
|
// position(RIGHT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
|
|
// position(FRONT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
|
|
// }
|
|
module position(from)
|
|
{
|
|
assert($parent_size != undef, "No object to attach to!");
|
|
anchors = (is_vector(from)||is_string(from))? [from] : from;
|
|
for (anchr = anchors) {
|
|
anch = find_anchor(anchr, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
|
$attach_to = undef;
|
|
$attach_anchor = anch;
|
|
$attach_norot = true;
|
|
translate(anch[1]) children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: attach()
|
|
// Usage:
|
|
// attach(from, [overlap]) ...
|
|
// attach(from, to, [overlap]) ...
|
|
// Description:
|
|
// Attaches children to a parent object at an anchor point and orientation.
|
|
// Attached objects will be overlapped into the parent object by a little bit,
|
|
// as specified by the default `$overlap` value (0.01 by default), or by the
|
|
// overriding `overlap=` argument. This is to prevent OpenSCAD from making
|
|
// non-manifold objects. You can also define `$overlap=` as an argument in a
|
|
// parent module to set the default for all attachments to it.
|
|
// Arguments:
|
|
// from = The vector, or name of the parent anchor point to attach to.
|
|
// to = Optional name of the child anchor point. If given, orients the child such that the named anchors align together rotationally.
|
|
// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0.01` by default.
|
|
// norot = If true, don't rotate children when attaching to the anchor point. Only translate to the anchor point.
|
|
// Example:
|
|
// spheroid(d=20) {
|
|
// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, anchor=BOTTOM);
|
|
// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5);
|
|
// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5);
|
|
// }
|
|
module attach(from, to=undef, overlap=undef, norot=false)
|
|
{
|
|
assert($parent_size != undef, "No object to attach to!");
|
|
overlap = (overlap!=undef)? overlap : $overlap;
|
|
anchors = (is_vector(from)||is_string(from))? [from] : from;
|
|
for (anchr = anchors) {
|
|
anch = find_anchor(anchr, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
|
$attach_to = to;
|
|
$attach_anchor = anch;
|
|
$attach_norot = norot;
|
|
if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) {
|
|
translate(anch[1]) translate([0,0,-overlap]) children();
|
|
} else {
|
|
fromvec = $parent_2d? BACK : UP;
|
|
translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate([0,0,-overlap]) children();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Module: edge_profile()
|
|
// Usage:
|
|
// edge_profile([edges], [except], [convexity]) ...
|
|
// Description:
|
|
// Takes a 2D mask shape and attaches it to the selected edges, with the appropriate orientation
|
|
// and extruded length to be `diff()`ed away, to give the edge a matching profile.
|
|
// Arguments:
|
|
// edges = Edges to mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges.
|
|
// except = Edges to explicitly NOT mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges.
|
|
// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10
|
|
// Side Effects:
|
|
// Sets `$tags = "mask"` for all children.
|
|
// Example:
|
|
// diff("mask")
|
|
// cube([50,60,70],center=true)
|
|
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
|
// mask2d_roundover(r=10, inset=2);
|
|
module edge_profile(edges=EDGES_ALL, except=[], convexity=10) {
|
|
assert($parent_size != undef, "No object to attach to!");
|
|
edges = edges(edges, except=except);
|
|
vecs = [
|
|
for (i = [0:3], axis=[0:2])
|
|
if (edges[axis][i]>0)
|
|
EDGE_OFFSETS[axis][i]
|
|
];
|
|
for (vec = vecs) {
|
|
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
|
assert(vcount == 2, "Not an edge vector!");
|
|
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
|
$attach_to = undef;
|
|
$attach_anchor = anch;
|
|
$attach_norot = true;
|
|
$tags = "mask";
|
|
length = sum(vmul($parent_size, [for (x=vec) x?0:1]))+0.1;
|
|
rotang =
|
|
vec.z<0? [90,0,180+vang(point2d(vec))] :
|
|
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) :
|
|
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] :
|
|
[-90,0,180+vang(point2d(vec))];
|
|
translate(anch[1]) {
|
|
rot(rotang) {
|
|
linear_extrude(height=length, center=true, convexity=convexity) {
|
|
children();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Module: edge_mask()
|
|
// Usage:
|
|
// edge_mask([edges], [except]) ...
|
|
// Description:
|
|
// Takes a 3D mask shape, and attaches it to the given edges, with the
|
|
// appropriate orientation to be `diff()`ed away.
|
|
// Arguments:
|
|
// edges = Edges to mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges.
|
|
// except = Edges to explicitly NOT mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges.
|
|
// Side Effects:
|
|
// Sets `$tags = "mask"` for all children.
|
|
// Example:
|
|
// diff("mask")
|
|
// cube([50,60,70],center=true)
|
|
// edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
|
|
// rounding_mask_z(l=71,r=10);
|
|
module edge_mask(edges=EDGES_ALL, except=[]) {
|
|
assert($parent_size != undef, "No object to attach to!");
|
|
edges = edges(edges, except=except);
|
|
vecs = [
|
|
for (i = [0:3], axis=[0:2])
|
|
if (edges[axis][i]>0)
|
|
EDGE_OFFSETS[axis][i]
|
|
];
|
|
for (vec = vecs) {
|
|
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
|
assert(vcount == 2, "Not an edge vector!");
|
|
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
|
$attach_to = undef;
|
|
$attach_anchor = anch;
|
|
$attach_norot = true;
|
|
$tags = "mask";
|
|
rotang =
|
|
vec.z<0? [90,0,180+vang(point2d(vec))] :
|
|
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) :
|
|
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] :
|
|
[-90,0,180+vang(point2d(vec))];
|
|
translate(anch[1]) rot(rotang) children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: corner_mask()
|
|
// Usage:
|
|
// corner_mask([corners], [except]) ...
|
|
// Description:
|
|
// Takes a 3D mask shape, and attaches it to the given corners, with the appropriate
|
|
// orientation to be `diff()`ed away. The 3D corner mask shape should be designed to
|
|
// mask away the X+Y+Z+ octant.
|
|
// Arguments:
|
|
// corners = Edges to mask. See the docs for [`corners()`](edges.scad#corners) to see acceptable values. Default: All corners.
|
|
// except = Edges to explicitly NOT mask. See the docs for [`corners()`](edges.scad#corners) to see acceptable values. Default: No corners.
|
|
// Side Effects:
|
|
// Sets `$tags = "mask"` for all children.
|
|
// Example:
|
|
// diff("mask")
|
|
// cube(100, center=true)
|
|
// corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
|
|
// difference() {
|
|
// translate(-0.01*[1,1,1]) cube(20);
|
|
// translate([20,20,20]) sphere(r=20);
|
|
// }
|
|
module corner_mask(corners=CORNERS_ALL, except=[]) {
|
|
assert($parent_size != undef, "No object to attach to!");
|
|
corners = corners(corners, except=except);
|
|
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
|
|
for (vec = vecs) {
|
|
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
|
assert(vcount == 3, "Not an edge vector!");
|
|
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
|
$attach_to = undef;
|
|
$attach_anchor = anch;
|
|
$attach_norot = true;
|
|
$tags = "mask";
|
|
rotang = vec.z<0?
|
|
[ 0,0,180+vang(point2d(vec))-45] :
|
|
[180,0,-90+vang(point2d(vec))-45];
|
|
translate(anch[1]) rot(rotang) 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.
|
|
// Arguments:
|
|
// c = Color name or RGBA vector.
|
|
// 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.
|
|
// Example:
|
|
// hide("A") cube(50, anchor=CENTER, $tags="Main") {
|
|
// attach(LEFT, BOTTOM) cylinder(d=30, l=30, $tags="A");
|
|
// attach(RIGHT, BOTTOM) cylinder(d=30, l=30, $tags="B");
|
|
// }
|
|
module hide(tags="")
|
|
{
|
|
$tags_hidden = tags==""? [] : _str_char_split(tags, " ");
|
|
children();
|
|
}
|
|
|
|
|
|
// Module: show()
|
|
// Usage:
|
|
// show(tags) ...
|
|
// Description:
|
|
// Shows only children with the given tags.
|
|
// Example:
|
|
// show("A B") cube(50, anchor=CENTER, $tags="Main") {
|
|
// attach(LEFT, BOTTOM) cylinder(d=30, l=30, $tags="A");
|
|
// attach(RIGHT, BOTTOM) cylinder(d=30, l=30, $tags="B");
|
|
// }
|
|
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.
|
|
// Example:
|
|
// diff("neg", "pos", keep="axle")
|
|
// sphere(d=100, $tags="pos") {
|
|
// attach(CENTER) xcyl(d=40, h=120, $tags="axle");
|
|
// attach(CENTER) cube([40,120,100], anchor=CENTER, $tags="neg");
|
|
// }
|
|
// Example: Masking
|
|
// diff("mask")
|
|
// cube([80,90,100], center=true) {
|
|
// let(p = $parent_size*1.01, $tags="mask") {
|
|
// position([for (y=[-1,1],z=[-1,1]) [0,y,z]])
|
|
// rounding_mask_x(l=p.x, r=25);
|
|
// position([for (x=[-1,1],z=[-1,1]) [x,0,z]])
|
|
// rounding_mask_y(l=p.y, r=20);
|
|
// position([for (x=[-1,1],y=[-1,1]) [x,y,0]])
|
|
// rounding_mask_z(l=p.z, r=25);
|
|
// }
|
|
// }
|
|
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.
|
|
// Example:
|
|
// intersect("wheel", "mask", keep="axle")
|
|
// sphere(d=100, $tags="wheel") {
|
|
// attach(CENTER) cube([40,100,100], anchor=CENTER, $tags="mask");
|
|
// attach(CENTER) xcyl(d=40, h=100, $tags="axle");
|
|
// }
|
|
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();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: hulling()
|
|
// Usage:
|
|
// hulling(a, [keep]) ...
|
|
// Description:
|
|
// Takes the union of all children with tags that are in `a`, and hull()s them.
|
|
// 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` are
|
|
// unioned with the result.
|
|
// Arguments:
|
|
// a = String containing space delimited set of tag names of children.
|
|
// keep = String containing space delimited set of tag names of children to keep whole.
|
|
// Example:
|
|
// hulling("body")
|
|
// sphere(d=100, $tags="body") {
|
|
// attach(CENTER) cube([40,90,90], anchor=CENTER, $tags="body");
|
|
// attach(CENTER) xcyl(d=40, h=120, $tags="other");
|
|
// }
|
|
module hulling(a)
|
|
{
|
|
hull() show(a) children();
|
|
children();
|
|
}
|
|
|
|
|
|
|
|
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|