First pass at attachments support.

This commit is contained in:
Revar Desmera 2019-04-16 19:16:50 -07:00
parent 96739c3ea0
commit 6f342f450f
10 changed files with 861 additions and 174 deletions

View file

@ -342,5 +342,20 @@ function corner_edge_count(edges, v) =
);
// Default values for attachment code.
$color = undef;
$overlap = 0.01;
$attach_to = undef;
$attach_conn = ["center", V_ZERO, V_UP, 0];
$parent_size = undef;
$parent_size2 = undef;
$parent_shift = [0,0];
$parent_orient = ORIENT_Z;
$parent_align = "center";
$parent_conns = [];
$tags_shown = [];
$tags_hidden = [];
$tags = "";
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -175,4 +175,76 @@ module debug_polyhedron(points, faces, convexity=10, txtsize=1, disabled=false)
// Function: all_conns()
// 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) str(a,"-",b)] : [],
in_list(type,["cube","cylinder"])? [for (a=zs, b=xs) str(a,"-",b)] : [],
in_list(type,["cube"])? [for (a=ys, b=xs) str(a,"-",b)] : [],
in_list(type,["cube"])? [for (a=zs, b=ys, c=xs) str(a,"-",b,"-",c)] : []
);
// Module: connector_arrow()
// Usage:
// connector_arrow([s], [color], [flag]);
// Description:
// Show a connector orientation arrow.
// Arguments:
// s = Length of the arrows.
// color = Color of the arrow.
// flag = If true, draw the orientation flag on the arrowhead.
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=V_UP)
attach("top") cyl(h=s/3, d1=s/5, d2=0, align=V_UP) {
if(flag) {
attach("bottom") recolor([1,0.5,0.5]) cuboid([s/50, s/6, s/4], align="front-top");
}
}
}
// 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)) {
attach(conn) connector_arrow();
}
children();
}
// Module: frameref()
// 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) {
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);
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

21
examples/attachments.scad Normal file
View file

@ -0,0 +1,21 @@
include <BOSL/constants.scad>
include <BOSL/transforms.scad>
include <BOSL/primitives.scad>
include <BOSL/shapes.scad>
include <BOSL/debug.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);
}
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]);
}
}
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

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

View file

@ -0,0 +1,42 @@
include <BOSL/constants.scad>
include <BOSL/transforms.scad>
include <BOSL/primitives.scad>
include <BOSL/beziers.scad>
module leaf(s) {
path = [
[0,0], [1.5,-1],
[2,1], [0,3], [-2,1],
[-1.5,-1], [0,0]
];
xrot(90)
linear_extrude_bezier(
scale_points(path, [s,s]/2),
height=0.02
);
}
module branches(minsize){
if($parent_size2.x>minsize) {
attach("top")
zrot(gaussian_rand(90,10))
zring(n=floor(log_rand(2,5,4)))
zrot(gaussian_rand(0,5))
yrot(gaussian_rand(30,5))
let(
sc = gaussian_rand(0.7,0.05),
s1 = $parent_size.z*sc,
s2 = $parent_size2.x
)
cylinder(d1=s2, d2=s2*sc, l=s1)
branches(minsize);
} else {
recolor("springgreen")
attach("top") zrot(90)
leaf(gaussian_rand(100,5));
}
}
recolor("lightgray") cylinder(d1=300, d2=250, l=1500) branches(5);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -0,0 +1,9 @@
include <BOSL/constants.scad>
include <BOSL/shapes.scad>
include <BOSL/debug.scad>
prismoid([60,40], [30,20], h=40) show_connectors();
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

18
examples/tagged_diff.scad Normal file
View file

@ -0,0 +1,18 @@
include <BOSL/constants.scad>
include <BOSL/transforms.scad>
include <BOSL/primitives.scad>
include <BOSL/shapes.scad>
diff("hole", "body pole")
sphere(d=100, $tags="body") {
zcyl(d=55, h=100, $tags="pole"); // attach() not needed for center-to-center.
tags("hole") {
xcyl(d=55, h=101);
ycyl(d=55, h=101);
}
zcyl(d=15, h=140, $tags="axle");
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

153
primitives.scad Normal file
View file

@ -0,0 +1,153 @@
//////////////////////////////////////////////////////////////////////
// LibFile: primitives.scad
// The basic built-in shapes, reworked to integrate better with
// other BOSL library shapes and utilities.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/primitives.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, 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.
*/
include <constants.scad>
include <compat.scad>
use <transforms.scad>
use <math.scad>
// Section: Primitive Shapes
// Module: cube()
//
// Description:
// Creates a cube object, with support for alignment and attachments.
// This is a drop-in replacement for the built-in `cube()` module.
//
// Arguments:
// size = The size of the cube.
// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
//
// Example: Simple regular cube.
// cube(40);
// Example: Rectangular cube, with given X, Y, and Z sizes.
// cuboid([20,40,50]);
module cube(size, center=undef, align=V_ALLPOS)
{
size = scalar_vec3(size);
orient_and_align(size, ORIENT_Z, align, center, noncentered=V_ALLPOS, chain=true) {
linear_extrude(height=size.z, convexity=2, center=true) {
square([size.x, size.y], center=true);
}
children();
}
}
// Module: cylinder()
// Usage:
// cylinder(h, r|d, [center], [orient], [align]);
// cylinder(h, r1/d1, r2/d2, [center], [orient], [align]);
// Description:
// Creates a cylinder object, with support for alignment and attachments.
// This is a drop-in replacement for the built-in `cylinder()` module.
// Arguments:
// l / h = The height of the cylinder.
// r = The radius of the cylinder.
// r1 = The bottom radius of the cylinder. (Before orientation.)
// r2 = The top radius of the cylinder. (Before orientation.)
// d = The diameter of the cylinder.
// d1 = The bottom diameter of the cylinder. (Before orientation.)
// d2 = The top diameter of the cylinder. (Before orientation.)
// orient = Orientation of the cylinder. Use the `ORIENT_` constants from `constants.scad`. Default: vertical.
// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
// Example: By Radius
// xdistribute(30) {
// cylinder(h=40, r=10);
// cylinder(h=40, r1=10, r2=5);
// }
// Example: By Diameter
// xdistribute(30) {
// 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=ALIGN_POS)
{
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=ALIGN_POS, chain=true) {
linear_extrude(height=l, scale=r2/r1, convexity=2, center=true) {
circle(r=r1, $fn=sides);
}
children();
}
}
// Module: sphere()
// Usage:
// sphere(r|d, [orient], [align])
// Description:
// Creates a sphere object, with support for alignment and attachments.
// This is a drop-in replacement for the built-in `sphere()` module.
// Arguments:
// r = Radius of the sphere.
// 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 `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// staggered_sphere(d=100);
module sphere(r=undef, d=undef, orient=ORIENT_Z, align=V_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) {
rotate_extrude(convexity=2) {
difference() {
circle(r=r, $fn=sides);
left(r+0.1) square(r*2+0.2, center=true);
}
}
children();
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -96,26 +96,25 @@ module cuboid(
if (is_def(p1)) {
if (is_def(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=V_ALLPOS);
cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS) children();
}
} else {
translate(p1) {
cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS);
cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS) children();
}
}
} else {
majrots = [[0,90,0], [90,0,0], [0,0,0]];
if (chamfer != undef) assertion(chamfer <= min(size)/2, "chamfer must be smaller than half the cube width, length, or height.");
if (fillet != undef) assertion(fillet <= min(size)/2, "fillet must be smaller than half the cube width, length, or height.");
algn = (!is_def(center))? (is_scalar(align)? align*V_UP : align) : (center==true)? V_CENTER : V_ALLPOS;
translate(vmul(size/2, algn)) {
majrots = [[0,90,0], [90,0,0], [0,0,0]];
orient_and_align(size, ORIENT_Z, align, center=center, noncentered=V_ALLPOS, chain=true) {
if (chamfer != undef) {
isize = [for (v = size) max(0.001, v-2*chamfer)];
if (edges == EDGES_ALL && trimcorners) {
hull() {
cube([size[0], isize[1], isize[2]], center=true);
cube([isize[0], size[1], isize[2]], center=true);
cube([isize[0], isize[1], size[2]], center=true);
cube([size.x, isize.y, isize.z], center=true);
cube([isize.x, size.y, isize.z], center=true);
cube([isize.x, isize.y, size.z], center=true);
}
} else {
difference() {
@ -201,6 +200,7 @@ module cuboid(
} else {
cube(size=size, center=true);
}
children();
}
}
}
@ -221,7 +221,7 @@ module cuboid(
// p2 = Coordinate point of opposite cube corner.
module cube2pt(p1,p2) {
deprecate("cube2pt()", "cuboid(p1,p2)");
cuboid(p1=p1, p2=p2);
cuboid(p1=p1, p2=p2) children();
}
@ -240,7 +240,7 @@ module cube2pt(p1,p2) {
// span_cube([0,15], [5,10], [0, 10]);
module span_cube(xspan, yspan, zspan) {
span = [xspan, yspan, zspan];
cuboid(p1=array_subindex(span,0), p2=array_subindex(span,1));
cuboid(p1=array_subindex(span,0), p2=array_subindex(span,1)) children();
}
@ -257,7 +257,7 @@ module span_cube(xspan, yspan, zspan) {
// v = vector to offset along.
module offsetcube(size=[1,1,1], v=[0,0,0]) {
deprecate("offsetcube()", "cuboid()");
cuboid(size=size, align=v);
cuboid(size=size, align=v) children();
}
@ -379,7 +379,7 @@ module chamfcube(size=[1,1,1], chamfer=0.25, chamfaxes=[1,1,1], chamfcorners=fal
(chamfaxes[1]? EDGES_Y_ALL : EDGES_NONE) +
(chamfaxes[2]? EDGES_Z_ALL : EDGES_NONE)
)
);
) children();
}
@ -396,7 +396,7 @@ module chamfcube(size=[1,1,1], chamfer=0.25, chamfaxes=[1,1,1], chamfcorners=fal
// center = If true, object will be centered. If false, sits on top of XY plane.
module rrect(size=[1,1,1], r=0.25, center=false) {
deprecate("rrect()", "cuboid()");
cuboid(size=size, fillet=r, edges=EDGES_Z_ALL, align=center? V_CENTER : V_UP);
cuboid(size=size, fillet=r, edges=EDGES_Z_ALL, align=center? V_CENTER : V_UP) children();
}
@ -413,7 +413,7 @@ module rrect(size=[1,1,1], r=0.25, center=false) {
// center = If true, object will be centered. If false, sits on top of XY plane.
module rcube(size=[1,1,1], r=0.25, center=false) {
deprecate("rcube()", "cuboid()");
cuboid(size=size, fillet=r, align=center? V_CENTER : V_UP);
cuboid(size=size, fillet=r, align=center? V_CENTER : V_UP) children();
}
@ -459,20 +459,20 @@ module prismoid(
orient=ORIENT_Z, align=ALIGN_POS, center=undef
) {
eps = 0.001;
s1 = [max(size1[0], eps), max(size1[1], eps)];
s2 = [max(size2[0], eps), max(size2[1], eps)];
shiftby = point3d(shift);
orient_and_align([s1[0], s1[1], h], orient, align, center, noncentered=ALIGN_POS) {
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=ALIGN_POS, chain=true) {
polyhedron(
points=[
[+s2[0]/2, +s2[1]/2, +h/2] + shiftby,
[+s2[0]/2, -s2[1]/2, +h/2] + shiftby,
[-s2[0]/2, -s2[1]/2, +h/2] + shiftby,
[-s2[0]/2, +s2[1]/2, +h/2] + shiftby,
[+s1[0]/2, +s1[1]/2, -h/2],
[+s1[0]/2, -s1[1]/2, -h/2],
[-s1[0]/2, -s1[1]/2, -h/2],
[-s1[0]/2, +s1[1]/2, -h/2],
[+s2.x/2, +s2.y/2, +h/2] + shiftby,
[+s2.x/2, -s2.y/2, +h/2] + shiftby,
[-s2.x/2, -s2.y/2, +h/2] + shiftby,
[-s2.x/2, +s2.y/2, +h/2] + shiftby,
[+s1.x/2, +s1.y/2, -h/2],
[+s1.x/2, -s1.y/2, -h/2],
[-s1.x/2, -s1.y/2, -h/2],
[-s1.x/2, +s1.y/2, -h/2],
],
faces=[
[0, 1, 2],
@ -490,6 +490,7 @@ module prismoid(
],
convexity=2
);
children();
}
}
@ -513,7 +514,7 @@ module prismoid(
// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP`.
module trapezoid(size1=[1,1], size2=[1,1], h=1, center=false) {
deprecate("trapezoid()", "prismoid()");
prismoid(size=size, size2=size2, h=h, center=center);
prismoid(size=size, size2=size2, h=h, center=center) children();
}
@ -548,13 +549,13 @@ module rounded_prismoid(
align=V_UP, orient=ORIENT_Z, center=undef
) {
eps = 0.001;
maxrad1 = min(size1[0]/2, size1[1]/2);
maxrad2 = min(size2[0]/2, size2[1]/2);
maxrad1 = min(size1.x/2, size1.y/2);
maxrad2 = min(size2.x/2, size2.y/2);
rr1 = min(maxrad1, (r1!=undef)? r1 : r);
rr2 = min(maxrad2, (r2!=undef)? r2 : r);
shiftby = point3d(shift);
orient_and_align([size1.x, size1.y, h], orient, align, center, noncentered=ALIGN_POS) {
down(h/2)
orient_and_align([size1.x, size1.y, h], orient, align, center, size2=size2, shift=shift, noncentered=ALIGN_POS, chain=true) {
down(h/2) {
hull() {
linear_extrude(height=eps, center=false, convexity=2) {
offset(r=rr1) {
@ -572,6 +573,8 @@ module rounded_prismoid(
}
}
}
children();
}
}
@ -596,7 +599,7 @@ module pyramid(n=4, h=1, l=1, r=undef, d=undef, circum=false)
{
deprecate("pyramid()", "cyl()");
radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
cyl(r1=radius, r2=0, l=h, circum=circum, $fn=n, realign=true, align=ALIGN_POS);
cyl(r1=radius, r2=0, l=h, circum=circum, $fn=n, realign=true, align=ALIGN_POS) children();
}
@ -620,7 +623,7 @@ module prism(n=3, h=1, l=1, r=undef, d=undef, circum=false, center=false)
{
deprecate("prism()", "cyl()");
radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
cyl(r=radius, l=h, circum=circum, $fn=n, realign=true, center=center);
cyl(r=radius, l=h, circum=circum, $fn=n, realign=true, center=center) children();
}
@ -644,36 +647,37 @@ module prism(n=3, h=1, l=1, r=undef, d=undef, circum=false, center=false)
// right_triangle([60, 10, 40]);
module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=V_ALLPOS, center=undef)
{
siz = scalar_vec3(size);
orient_and_align(siz, align=align, center=center) {
size = scalar_vec3(size);
orient_and_align(size, align=align, center=center, chain=true) {
if (orient == ORIENT_X) {
ang = atan2(siz[1], siz[2]);
masksize = [siz[0], siz[1], norm([siz[1],siz[2]])] + [1,1,1];
ang = atan2(size.y, size.z);
masksize = [size.x, size.y, norm([size.y,size.z])] + [1,1,1];
xrot(ang) {
difference() {
xrot(-ang) cube(siz, center=true);
back(masksize[1]/2) cube(masksize, center=true);
xrot(-ang) cube(size, center=true);
back(masksize.y/2) cube(masksize, center=true);
}
}
} else if (orient == ORIENT_Y) {
ang = atan2(siz[0], siz[2]);
masksize = [siz[0], siz[1], norm([siz[0],siz[2]])] + [1,1,1];
ang = atan2(size.x, size.z);
masksize = [size.x, size.y, norm([size.x,size.z])] + [1,1,1];
yrot(-ang) {
difference() {
yrot(ang) cube(siz, center=true);
right(masksize[0]/2) cube(masksize, center=true);
yrot(ang) cube(size, center=true);
right(masksize.x/2) cube(masksize, center=true);
}
}
} else if (orient == ORIENT_Z) {
ang = atan2(siz[0], siz[1]);
masksize = [norm([siz[0],siz[1]]), siz[1], siz[2]] + [1,1,1];
ang = atan2(size.x, size.y);
masksize = [norm([size.x,size.y]), size.y, size.z] + [1,1,1];
zrot(-ang) {
difference() {
zrot(ang) cube(siz, center=true);
back(masksize[1]/2) cube(masksize, center=true);
zrot(ang) cube(size, center=true);
back(masksize.y/2) cube(masksize, center=true);
}
}
}
children();
}
}
@ -778,9 +782,12 @@ module cyl(
r1 = get_radius(r1, r, d1, d, 1);
r2 = get_radius(r2, r, d2, d, 1);
l = first_defined([l, h, 1]);
size1 = [r1*2,r1*2,l];
size2 = [r2*2,r2*2,l];
sides = segs(max(r1,r2));
sc = circum? 1/cos(180/sides) : 1;
orient_and_align([r1*2,r1*2,l], orient, align, center=center) {
phi = atan2(l, r1-r2);
orient_and_align(size1, orient, align, center=center, size2=size2, 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);
@ -901,6 +908,7 @@ module cyl(
}
}
}
children();
}
}
@ -930,10 +938,8 @@ module cyl(
// 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)
{
h = first_defined([l, h, 1]);
down(h/2) {
cylinder(r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, h=h, center=true);
}
l = first_defined([l, h, 1]);
cyl(r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, l=l, align=V_DOWN) children();
}
@ -971,7 +977,7 @@ module downcyl(r=undef, h=undef, l=undef, d=undef, d1=undef, d2=undef, r1=undef,
// }
module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
{
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_X, align=align, center=center);
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_X, align=align, center=center) children();
}
@ -1009,7 +1015,7 @@ module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h
// }
module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
{
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Y, align=align, center=center);
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Y, align=align, center=center) children();
}
@ -1047,7 +1053,7 @@ module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h
// }
module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
{
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Z, align=align, center=center);
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Z, align=align, center=center) children();
}
@ -1075,7 +1081,7 @@ module chamferred_cylinder(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef,
deprecate("chamf_cyl()` and `chamferred_cylinder()", "cyl()");
r = get_radius(r=r, d=d, dflt=1);
chamf = (chamfedge == undef)? chamfer : chamfedge * cos(angle);
cyl(l=h, r=r, chamfer1=bottom? chamf : 0, chamfer2=top? chamf : 0, chamfang=angle, center=center);
cyl(l=h, r=r, chamfer1=bottom? chamf : 0, chamfer2=top? chamf : 0, chamfang=angle, center=center) children();
}
@ -1099,7 +1105,7 @@ module chamferred_cylinder(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef,
// bottom = boolean. If true, chamfer the bottom edges. (Default: True)
// center = boolean. If true, cylinder is centered. (Default: false)
module chamf_cyl(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
chamferred_cylinder(h=h, r=r, d=d, chamfer=chamfer, chamfedge=chamfedge, angle=angle, center=center, top=top, bottom=bottom);
chamferred_cylinder(h=h, r=r, d=d, chamfer=chamfer, chamfedge=chamfedge, angle=angle, center=center, top=top, bottom=bottom) children();
// Module: filleted_cylinder()
@ -1119,7 +1125,7 @@ module chamf_cyl(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45,
// center = boolean. If true, cylinder is centered. (Default: false)
module filleted_cylinder(h=1, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
deprecate("filleted_cylinder()", "cyl()");
cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center) children();
}
@ -1142,7 +1148,7 @@ module filleted_cylinder(h=1, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2
// center = boolean. If true, cylinder is centered. (Default: false)
module rcylinder(h=1, r=1, r1=undef, r2=undef, d=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
deprecate("rcylinder()", "cyl(..., fillet)");
cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center) children();
}
@ -1208,13 +1214,16 @@ module tube(
assertion(ir1 <= r1, "Inner radius is larger than outer radius.");
assertion(ir2 <= r2, "Inner radius is larger than outer radius.");
sides = segs(max(r1,r2));
orient_and_align([r1*2,r1*2,h], orient, align, center=center) {
size = [r1*2,r1*2,h];
size2 = [r2*2,r2*2,h];
orient_and_align(size, orient, align, center=center, size2=size2, chain=true) {
zrot(realign? 180/sides : 0) {
difference() {
cylinder(h=h, r1=r1, r2=r2, center=true, $fn=sides);
cylinder(h=h+0.05, r1=ir1, r2=ir2, center=true);
cyl(h=h, r1=r1, r2=r2, $fn=sides) children();
cyl(h=h+0.05, r1=ir1, r2=ir2);
}
}
children();
}
}
@ -1257,10 +1266,12 @@ module torus(
irr = get_radius(r=ir, d=id, dflt=0.5);
majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2);
minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2);
orient_and_align([(majrad+minrad)*2, (majrad+minrad)*2, minrad*2], orient, align, center=center) {
size = [(majrad+minrad)*2, (majrad+minrad)*2, minrad*2];
orient_and_align(size, orient, align, center=center, chain=true) {
rotate_extrude(convexity=4) {
right(majrad) circle(minrad);
}
children();
}
}
@ -1269,6 +1280,34 @@ module torus(
// Section: Spheroids
// Module: spheroid()
// Description:
// An version of `sphere()` with connector points, orientation, and alignment.
// Usage:
// spheroid(r|d, [circum])
// Arguments:
// r = Radius of the sphere.
// d = Diameter of the sphere.
// 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 `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// spheroid(d=100, circum=true, $fn=10);
module spheroid(r=undef, d=undef, circum=false, orient=V_UP, align=V_CENTER)
{
r = get_radius(r=r, d=d, dflt=1);
hsides = segs(r);
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) {
sphere(r=rr);
children();
}
}
// Module: staggered_sphere()
//
// Description:
@ -1281,16 +1320,18 @@ module torus(
// r = Radius of the sphere.
// d = Diameter of the sphere.
// circum = If true, circumscribes the perfect sphere of the given size.
// 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 `V_` constants from `constants.scad`. Default: `V_CENTER`.
//
// Example:
// staggered_sphere(d=100, circum=true, $fn=10);
module staggered_sphere(r=undef, d=undef, circum=false, align=V_CENTER) {
module staggered_sphere(r=undef, d=undef, circum=false, orient=V_UP, align=V_CENTER) {
r = get_radius(r=r, d=d, dflt=1);
sides = segs(r);
vsides = max(3, ceil(sides/2))+1;
step = 360/sides;
vstep = 180/(vsides-1);
rr = circum? r/cos(180/sides)/cos(180/sides) : r;
rr = circum? (r / cos(180/sides) / cos(90/vsides)) : r;
pts = concat(
[[0,0,rr]],
[
@ -1320,7 +1361,11 @@ module staggered_sphere(r=undef, d=undef, circum=false, align=V_CENTER) {
) each [[v1,v4,v3], [v1,v2,v4]]
]
);
size = [2*rr, 2*rr, 2*rr];
orient_and_align(size, orient, align, chain=true) {
polyhedron(points=pts, faces=faces);
children();
}
}
@ -1394,10 +1439,12 @@ 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]);
orient_and_align([r*2,r*2,l], orient, align) {
size = [r*2,r*2,l];
orient_and_align(size, orient, align, chain=true) {
linear_extrude(height=l, center=true, slices=2) {
teardrop2d(r=r, ang=ang, cap_h=cap_h);
}
children();
}
}
@ -1429,13 +1476,15 @@ module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, orient=ORIENT_Z,
r = get_radius(r=r, d=d, dflt=1);
h = first_defined([cap_h, h]);
maxd = 3*r/tan(maxang);
orient_and_align([r*2,r*2,r*2], orient, align) {
size = [r*2,r*2,r*2];
orient_and_align(size, orient, align, chain=true) {
rotate_extrude(convexity=2) {
difference() {
teardrop2d(r=r, ang=maxang, cap_h=h);
left(r) square(size=[2*r,maxd], center=true);
}
}
children();
}
}
@ -1464,7 +1513,8 @@ module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, orient=ORIENT_Z,
module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=V_UP)
{
h = wall + w/2/tan(ang);
orient_and_align([w, h, l], orient, align, orig_orient=ORIENT_Z) {
size = [w, h, l];
orient_and_align(size, orient, align, chain=true) {
fwd(h/2) {
linear_extrude(height=l, center=true, slices=2) {
back(wall/2) square([w, wall], center=true);
@ -1478,6 +1528,7 @@ module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=V_UP)
}
}
}
children();
}
}
@ -1505,7 +1556,7 @@ module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=V_UP)
// thinning_wall(h=50, l=80, thick=4);
// Example: Trapezoidal
// thinning_wall(h=50, l=[80,50], thick=4);
module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_X, align=V_CENTER)
module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_Z, align=V_CENTER)
{
l1 = (l[0] == undef)? l : l[0];
l2 = (l[1] == undef)? l : l[1];
@ -1528,7 +1579,8 @@ module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIEN
y1 = thick/2;
y2 = y1 - min(z2-z3, x2-x3) * sin(ang);
orient_and_align([l1, thick, h], orient, align, orig_orient=ORIENT_X) {
size = [l1, thick, h];
orient_and_align(size, orient, align, size2=[l2,thick], chain=true) {
polyhedron(
points=[
[-x4, -y1, -z1],
@ -1620,6 +1672,7 @@ module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIEN
],
convexity=6
);
children();
}
}
@ -1649,7 +1702,9 @@ module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orien
{
dang = atan((h-2*strut)/(l-2*strut));
dlen = (h-2*strut)/sin(dang);
orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
size = [l, thick, h];
orient_and_align(size, orient, align, orig_orient=ORIENT_Y, chain=true) {
union() {
xrot_copies([0, 180]) {
down(h/2) narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
fwd(l/2) xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
@ -1667,6 +1722,8 @@ module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orien
}
cube(size=[wall, l-0.1, h-0.1], center=true);
}
children();
}
}
@ -1702,7 +1759,8 @@ module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly
{
dang = atan(h/l);
dlen = h/sin(dang);
orient_and_align([thick, l, h], orient, align, center=center, noncentered=V_UP+V_BACK, orig_orient=ORIENT_Y) {
size = [thick, l, h];
orient_and_align(size, orient, align, center=center, noncentered=V_UP+V_BACK, orig_orient=ORIENT_Y, chain=true) {
difference() {
union() {
if (!diagonly) {
@ -1725,6 +1783,7 @@ module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly
}
}
}
children();
}
}
@ -1750,7 +1809,7 @@ module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly
module thinning_brace(h=50, l=100, thick=5, ang=30, strut=5, wall=3, center=true)
{
deprecate("thinning_brace()", "thinning_triangle(..., diagonly=true)");
thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true, center=center);
thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true, center=center) children();
}
@ -1801,7 +1860,9 @@ module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, ori
ang = atan(ystep/zstep);
len = zstep / cos(ang);
orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
size = [thick, l, h];
orient_and_align(size, orient, align, orig_orient=ORIENT_Y, chain=true) {
union() {
zspread(zoff*2)
cube(size=[thick, l, strut], center=true);
yspread(yoff*2)
@ -1813,6 +1874,8 @@ module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, ori
}
}
}
children();
}
}
@ -1866,7 +1929,8 @@ module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge
supp_reps = floor(cross_len/2/(zstep*sin(supp_ang)));
supp_step = cross_len/2/supp_reps;
orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y) {
size = [w, l, h];
orient_and_align(size, orient, align, orig_orient=ORIENT_Y, chain=true) {
intersection() {
union() {
ybridge = (l - (yreps+1) * strut) / yreps;
@ -1908,6 +1972,7 @@ module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge
}
cube([w,l,h], center=true);
}
children();
}
}
@ -1943,7 +2008,9 @@ module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, a
steps = quantup(segs(thick/2),4);
step = period/steps;
il = l - 2*strut + 2*step;
orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
size = [thick, l, h];
orient_and_align(size, orient, align, orig_orient=ORIENT_Y, chain=true) {
union() {
linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) {
polygon(
points=concat(
@ -1952,12 +2019,13 @@ module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, a
)
);
}
difference() {
cube([thick, l, h], center=true);
cube([thick+0.5, l-2*strut, h-2*strut], center=true);
}
}
children();
}
}
@ -2016,7 +2084,8 @@ module pie_slice(
r1 = get_radius(r1, r, d1, d, 10);
r2 = get_radius(r2, r, d2, d, 10);
maxd = max(r1,r2)+0.1;
orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
size = [2*r1, 2*r1, l];
orient_and_align(size, orient, align, center=center, 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);
@ -2025,6 +2094,7 @@ module pie_slice(
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
}
}
children();
}
}
@ -2057,7 +2127,8 @@ module pie_slice(
// interior_fillet(l=40, r=10, orient=ORIENT_Y_90);
module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, orient=ORIENT_X, align=V_CENTER) {
dy = r/tan(ang/2);
orient_and_align([l,r,r], orient, align, orig_orient=ORIENT_X) {
size = [l,r,r];
orient_and_align(size, orient, align, orig_orient=ORIENT_X, chain=true) {
difference() {
translate([0,-overlap/tan(ang/2),-overlap]) {
if (ang == 90) {
@ -2068,6 +2139,7 @@ module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, orient=ORIENT_X, alig
}
translate([0,dy,r]) xcyl(l=l+0.1, r=r);
}
children();
}
}
@ -2108,6 +2180,8 @@ module slot(
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=5);
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=5);
sides = quantup(segs(max(r1, r2)), 4);
// TODO: implement orient and align.
// TODO: implement connectors.
hull() spread(p1=p1, p2=p2, l=l, n=2) cyl(l=h, r1=r1, r2=r2, center=true, $fn=sides);
}
@ -2156,7 +2230,8 @@ module arced_slot(
sr2 = get_radius(sr2, sr, sd2, sd, 2);
fn_minor = first_defined([$fn2, $fn]);
da = ea - sa;
orient_and_align([r+sr1, r+sr1, h], orient, align) {
size = [r+sr1, r+sr1, h];
orient_and_align(size, orient, align, chain=true) {
translate(cp) {
zrot(sa) {
difference() {
@ -2167,6 +2242,7 @@ module arced_slot(
zrot(da) right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
}
}
children();
}
}

View file

@ -1439,27 +1439,22 @@ module zrot_copies(rots=[], cp=[0,0,0], n=undef, count=undef, sa=0, offset=0, r=
// Module: xring()
//
// Description:
// Distributes `n` copies of the given children on a circle of radius `r`
// around the X axis. If `rot` is true, each copy is rotated in place to keep
// the same side towards the center. The first, unrotated copy will be at the
// starting angle `sa`.
//
// Usage:
// xring(n, r, [sa], [cp], [rot]) ...
//
// Arguments:
// n = Number of copies of children to distribute around the circle. (Default: 2)
// r = Radius of ring to distribute children around. (Default: 0)
// sa = Start angle for first (unrotated) copy. (Default: 0)
// cp = Centerpoint of ring. Default: [0,0,0]
// rot = If true, rotate each copy to keep the same side towards the center of the ring. Default: true.
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
//
// Examples:
// xring(n=6, r=10) xrot(-90) cylinder(h=20, r1=5, r2=0);
// xring(n=6, r=10, sa=45) xrot(-90) cylinder(h=20, r1=5, r2=0);
@ -2274,17 +2269,28 @@ module shell2d(thickness, or=0, ir=0, fill=0, round=0)
// Named alignments, as well as `ALIGN_NEG`/`ALIGN_POS` are aligned pre-rotation.
//
// Usage:
// orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments]) ...
// orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments], [chain]) ...
//
// Arguments:
// size = The size of the part.
// orient = The axis to align to. Use ORIENT_ constants from constants.scad
// 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: `V_UP`.
// orig_orient = The original orientation of the part. Default: `ORIENT_Z`.
// orig_align = The original alignment of the part. Default: `V_CENTER`.
// alignments = A list of `["name", [X,Y,Z]]` alignment-label/offset pairs.
// 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);
@ -2293,55 +2299,321 @@ module orient_and_align(
size=undef, orient=ORIENT_Z, align=V_CENTER,
center=undef, noncentered=ALIGN_POS,
orig_orient=ORIENT_Z, orig_align=V_CENTER,
alignments=[]
size2=undef, shift=[0,0],
alignments=[], chain=false
) {
algn = is_def(center)? (center? V_CENTER : noncentered) : align;
if (orig_align != V_CENTER) {
orient_and_align(size=size, orient=orient, align=algn) {
translate(vmul(size/2, -orig_align)) children();
}
} else if (orig_orient != ORIENT_Z) {
rotsize = (
(orig_orient==ORIENT_X)? [size[1], size[2], size[0]] :
(orig_orient==ORIENT_Y)? [size[0], size[2], size[1]] :
vabs(rotate_points3d([size], orig_orient, reverse=true)[0])
);
orient_and_align(size=rotsize, orient=orient, align=algn) {
rot(orig_orient,reverse=true) children();
}
} else if (is_scalar(algn)) {
// If align is a number and not a vector, then translate PRE-rotation.
orient_and_align(size=size, orient=orient) {
translate(vmul(size/2, algn*V_UP)) children();
}
} else if (is_str(algn)) {
// If align is a string, look for an alignments label that matches.
found = search([algn], alignments, num_returns_per_match=1);
if (found != [[]]) {
orient_and_align(size=size, orient=orient) {
idx = found[0];
delta = alignments[idx][1];
translate(-delta) children();
}
size2 = point2d(default(size2, size));
shift = point2d(shift);
align = is_def(center)? (center? V_CENTER : noncentered) : align;
m = matrix4_mult(concat(
(orig_align==V_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],V_DOWN),
axis = vector_axis(conn[2],V_DOWN),
ang2 = (conn[2]==V_UP || conn[2]==V_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(
(!is_scalar(align) && !is_str(align))? [] : [
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)
],
(!is_array(align) || align==[0,0,0])? [] : [
let(conn = find_connector(align, size.z, size, size2=size2, shift=shift))
matrix4_translate(conn[1])
]
)
));
$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 {
assertion(1==0, str("Alignment label '", algn, "' is not known.", (alignments? str(" Try one of ", [for (v=alignments) v[0]], ".") : "")));
if(shown && !hidden) color($color) children();
}
} else if (orient != ORIENT_Z) {
rotsize = (
(orient==ORIENT_X)? [size[2], size[0], size[1]] :
(orient==ORIENT_Y)? [size[0], size[2], size[1]] :
vabs(rotate_points3d([size], orient)[0])
);
orient_and_align(size=rotsize, align=algn) {
rotate(orient) children();
}
} else if (is_def(algn) && algn != [0,0,0]) {
translate(vmul(size/2, algn)) children();
} else {
children();
}
}
// Internal. Not exposed.
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]));
// 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=V_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(
eps = 1e-9,
shift = point3d(shift),
size = point3d(point2d(size)),
size2 = (size2!=undef)? point3d(point2d(size2)) : size,
found = !is_str(align)? [] : search([align], extra_conns, num_returns_per_match=1)[0]
) (found!=[])? extra_conns[found] : let(
words = is_scalar(align)? (
align==ALIGN_NEG? ["top"] :
align==ALIGN_POS? ["bottom"] :
["center"]
) : is_array(align)? align : _str_char_split(align,"-"),
ovec = is_array(align)? align :
sum([
for (word = words)
word=="left"? V_LEFT :
word=="right"? V_RIGHT :
word=="front"? V_FWD :
word=="back"? V_BACK :
word=="top"? V_UP :
word=="bottom"? V_DOWN :
word=="center"? V_ZERO :
assertion(false,
str(
"Alignment label '", align, "' is not known.",
(!extra_conns? "" : str(
" Try one of ", [for (v=extra_conns) v[0]], " or the standard alignments."
))
)
)
]),
top = [-size2/2+shift, shift, size2/2+shift],
bot = [-size/2, V_ZERO, size/2],
toppt = [top[ovec.x+1].x, top[ovec.y+1].y, h/2],
botpt = [bot[ovec.x+1].x, bot[ovec.y+1].y, -h/2],
pos = lerp(botpt, toppt, (ovec.z+1)/2),
oang = (
ovec == V_UP? 0 :
ovec == V_DOWN? 0 :
(norm([ovec.x,ovec.y]) < eps)? 0 : atan2(ovec.y, ovec.x)+90
),
vec = (
abs(ovec.z) > eps? ovec :
rotate_points3d([ovec], from=V_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)
{
assertion($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-V_UP)<1e-9 && ang==0)) {
translate(pos) translate([0,0,-overlap]) children();
} else {
translate(pos) rot(ang,from=V_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