mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
1939 lines
62 KiB
OpenSCAD
1939 lines
62 KiB
OpenSCAD
//////////////////////////////////////////////////////////////////////
|
|
// LibFile: shapes.scad
|
|
// Common useful shapes and structured objects.
|
|
// 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: Cuboids
|
|
|
|
|
|
// Module: cuboid()
|
|
//
|
|
// Description:
|
|
// Creates a cube or cuboid object, with optional chamfering or filleting/rounding.
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube.
|
|
// chamfer = Size of chamfer, inset from sides. Default: No chamferring.
|
|
// fillet = Radius of fillet for edge rounding. Default: No filleting.
|
|
// edges = Edges to chamfer/fillet. Use `EDGE` constants from constants.scad. Default: `EDGES_ALL`
|
|
// trimcorners = If true, rounds or chamfers corners where three chamferred/filleted edges meet. Default: `true`
|
|
// p1 = Align the cuboid's corner at `p1`, if given. Forces `align=UP+BACK+RIGHT`.
|
|
// p2 = If given with `p1`, defines the cornerpoints of the cuboid.
|
|
// 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+BACK+RIGHT`.
|
|
//
|
|
// Example: Simple regular cube.
|
|
// cuboid(40);
|
|
// Example: Cube with minimum cornerpoint given.
|
|
// cuboid(20, p1=[10,0,0]);
|
|
// Example: Rectangular cube, with given X, Y, and Z sizes.
|
|
// cuboid([20,40,50]);
|
|
// Example: Rectangular cube defined by opposing cornerpoints.
|
|
// cuboid(p1=[0,10,0], p2=[20,30,30]);
|
|
// Example: Rectangular cube with chamferred edges and corners.
|
|
// cuboid([30,40,50], chamfer=5);
|
|
// Example: Rectangular cube with chamferred edges, without trimmed corners.
|
|
// cuboid([30,40,50], chamfer=5, trimcorners=false);
|
|
// Example: Rectangular cube with rounded edges and corners.
|
|
// cuboid([30,40,50], fillet=10);
|
|
// Example: Rectangular cube with rounded edges, without trimmed corners.
|
|
// cuboid([30,40,50], fillet=10, trimcorners=false);
|
|
// Example: Rectangular cube with only some edges chamferred.
|
|
// 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);
|
|
module cuboid(
|
|
size=[1,1,1],
|
|
p1=undef, p2=undef,
|
|
chamfer=undef,
|
|
fillet=undef,
|
|
edges=EDGES_ALL,
|
|
trimcorners=true,
|
|
align=[0,0,0],
|
|
center=undef
|
|
) {
|
|
size = scalar_vec3(size);
|
|
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();
|
|
}
|
|
} else {
|
|
translate(p1) {
|
|
cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLPOS) children();
|
|
}
|
|
}
|
|
} else {
|
|
if (chamfer != undef) assert(chamfer <= min(size)/2, "chamfer must be smaller than half the cube width, length, or height.");
|
|
if (fillet != undef) assert(fillet <= min(size)/2, "fillet must be smaller than half the cube width, length, or height.");
|
|
majrots = [[0,90,0], [90,0,0], [0,0,0]];
|
|
orient_and_align(size, ORIENT_Z, align, center=center, noncentered=ALLPOS, chain=true) {
|
|
if (chamfer != undef) {
|
|
isize = [for (v = size) max(0.001, v-2*chamfer)];
|
|
if (edges == EDGES_ALL && trimcorners) {
|
|
hull() {
|
|
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() {
|
|
cube(size, center=true);
|
|
|
|
// Chamfer edges
|
|
for (i = [0:3], axis=[0:2]) {
|
|
if (edges[axis][i]>0) {
|
|
translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
|
|
rotate(majrots[axis]) {
|
|
zrot(45) cube([chamfer*sqrt(2), chamfer*sqrt(2), size[axis]+0.01], center=true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Chamfer triple-edge corners.
|
|
if (trimcorners) {
|
|
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
|
if (corner_edge_count(edges, [xa,ya,za]) > 2) {
|
|
translate(vmul([xa,ya,za]/2, size-[1,1,1]*chamfer*4/3)) {
|
|
rot(from=UP, to=[xa,ya,za]) {
|
|
upcube(chamfer*3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (fillet != undef) {
|
|
sides = quantup(segs(fillet),4);
|
|
sc = 1/cos(180/sides);
|
|
isize = [for (v = size) max(0.001, v-2*fillet)];
|
|
if (edges == EDGES_ALL) {
|
|
minkowski() {
|
|
cube(isize, center=true);
|
|
if (trimcorners) {
|
|
sphere(r=fillet*sc, $fn=sides);
|
|
} else {
|
|
intersection() {
|
|
zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
|
|
rotate([90,0,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
|
|
rotate([0,90,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
difference() {
|
|
cube(size, center=true);
|
|
|
|
// Round edges.
|
|
for (i = [0:3], axis=[0:2]) {
|
|
if (edges[axis][i]>0) {
|
|
difference() {
|
|
translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
|
|
rotate(majrots[axis]) cube([fillet*2, fillet*2, size[axis]+0.1], center=true);
|
|
}
|
|
translate(vmul(EDGE_OFFSETS[axis][i], size/2 - [1,1,1]*fillet)) {
|
|
rotate(majrots[axis]) zrot(180/sides) cylinder(h=size[axis]+0.2, r=fillet*sc, center=true, $fn=sides);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Round triple-edge corners.
|
|
if (trimcorners) {
|
|
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
|
if (corner_edge_count(edges, [xa,ya,za]) > 2) {
|
|
difference() {
|
|
translate(vmul([xa,ya,za], size/2)) {
|
|
cube(fillet*2, center=true);
|
|
}
|
|
translate(vmul([xa,ya,za], size/2-[1,1,1]*fillet)) {
|
|
zrot(180/sides) sphere(r=fillet*sc*sc, $fn=sides);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
cube(size=size, center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: leftcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the left side of the origin.
|
|
//
|
|
// Usage:
|
|
// leftcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// leftcube([20,30,40]);
|
|
module leftcube(size=[1,1,1]) {siz = scalar_vec3(size); left(siz[0]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Module: rightcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the right side of the origin.
|
|
//
|
|
// Usage:
|
|
// rightcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// rightcube([20,30,40]);
|
|
module rightcube(size=[1,1,1]) {siz = scalar_vec3(size); right(siz[0]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Module: fwdcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the front side of the origin.
|
|
//
|
|
// Usage:
|
|
// fwdcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// fwdcube([20,30,40]);
|
|
module fwdcube(size=[1,1,1]) {siz = scalar_vec3(size); fwd(siz[1]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Module: backcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the front side of the origin.
|
|
//
|
|
// Usage:
|
|
// backcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// backcube([20,30,40]);
|
|
module backcube(size=[1,1,1]) {siz = scalar_vec3(size); back(siz[1]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Module: downcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the bottom side of the origin.
|
|
//
|
|
// Usage:
|
|
// downcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// downcube([20,30,40]);
|
|
module downcube(size=[1,1,1]) {siz = scalar_vec3(size); down(siz[2]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Module: upcube()
|
|
//
|
|
// Description:
|
|
// Makes a cube that is aligned on the top side of the origin.
|
|
//
|
|
// Usage:
|
|
// upcube(size);
|
|
//
|
|
// Arguments:
|
|
// size = The size of the cube to make.
|
|
//
|
|
// Example:
|
|
// upcube([20,30,40]);
|
|
module upcube(size=[1,1,1]) {siz = scalar_vec3(size); up(siz[2]/2) cube(size=size, center=true);}
|
|
|
|
|
|
// Section: Prismoids
|
|
|
|
|
|
// Module: prismoid()
|
|
//
|
|
// Description:
|
|
// Creates a rectangular prismoid shape.
|
|
//
|
|
// Usage:
|
|
// prismoid(size1, size2, h, [shift], [orient], [align|center]);
|
|
//
|
|
// Arguments:
|
|
// size1 = [width, length] of the axis-negative end of the prism.
|
|
// size2 = [width, length] of the axis-positive end of the prism.
|
|
// h = Height of the prism.
|
|
// shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
|
|
// orient = Orientation of the prismoid. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
|
|
// align = Alignment of the prismoid by the axis-negative (size1) end. Use the constants from `constants.scad`. Default: `UP`.
|
|
// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=UP`.
|
|
//
|
|
// Example: Rectangular Pyramid
|
|
// prismoid(size1=[40,40], size2=[0,0], h=20);
|
|
// Example: Prism
|
|
// prismoid(size1=[40,40], size2=[0,40], h=20);
|
|
// Example: Truncated Pyramid
|
|
// prismoid(size1=[35,50], size2=[20,30], h=20);
|
|
// Example: Wedge
|
|
// prismoid(size1=[60,35], size2=[30,0], h=30);
|
|
// Example: Truncated Tetrahedron
|
|
// prismoid(size1=[10,40], size2=[40,10], h=40);
|
|
// Example: Inverted Truncated Pyramid
|
|
// prismoid(size1=[15,5], size2=[30,20], h=20);
|
|
// Example: Right Prism
|
|
// 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]);
|
|
module prismoid(
|
|
size1=[1,1], size2=[1,1], h=1, shift=[0,0],
|
|
orient=ORIENT_Z, align=UP, 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) {
|
|
polyhedron(
|
|
points=[
|
|
[+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],
|
|
[0, 2, 3],
|
|
[0, 4, 5],
|
|
[0, 5, 1],
|
|
[1, 5, 6],
|
|
[1, 6, 2],
|
|
[2, 6, 7],
|
|
[2, 7, 3],
|
|
[3, 7, 4],
|
|
[3, 4, 0],
|
|
[4, 7, 6],
|
|
[4, 6, 5],
|
|
],
|
|
convexity=2
|
|
);
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: rounded_prismoid()
|
|
//
|
|
// Description:
|
|
// Creates a rectangular prismoid shape with rounded vertical edges.
|
|
//
|
|
// Arguments:
|
|
// size1 = [width, length] of the bottom of the prism.
|
|
// size2 = [width, length] of the top of the prism.
|
|
// h = Height of the prism.
|
|
// r = radius of vertical edge fillets.
|
|
// r1 = radius of vertical edge fillets at bottom.
|
|
// r2 = radius of vertical edge fillets at top.
|
|
// shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
|
|
// orient = Orientation of the prismoid. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
|
|
// align = Alignment of the prismoid by the axis-negative (`size1`) end. Use the constants from `constants.scad`. Default: `UP`.
|
|
// center = vertically center the prism. Overrides `align`.
|
|
//
|
|
// Example: Rounded Pyramid
|
|
// rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5);
|
|
// Example: Centered Rounded Pyramid
|
|
// rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5, center=true);
|
|
// Example: Disparate Top and Bottom Radii
|
|
// 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);
|
|
module rounded_prismoid(
|
|
size1, size2, h, shift=[0,0],
|
|
r=undef, r1=undef, r2=undef,
|
|
align=UP, orient=ORIENT_Z, center=undef
|
|
) {
|
|
eps = 0.001;
|
|
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, size2=size2, shift=shift, noncentered=UP, chain=true) {
|
|
down(h/2) {
|
|
hull() {
|
|
linear_extrude(height=eps, center=false, convexity=2) {
|
|
offset(r=rr1) {
|
|
square([max(eps, size1[0]-2*rr1), max(eps, size1[1]-2*rr1)], center=true);
|
|
}
|
|
}
|
|
up(h-0.01) {
|
|
translate(shiftby) {
|
|
linear_extrude(height=eps, center=false, convexity=2) {
|
|
offset(r=rr2) {
|
|
square([max(eps, size2[0]-2*rr2), max(eps, size2[1]-2*rr2)], center=true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: right_triangle()
|
|
//
|
|
// Description:
|
|
// Creates a 3D right triangular prism.
|
|
//
|
|
// Usage:
|
|
// right_triangle(size, [orient], [align|center]);
|
|
//
|
|
// 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`.
|
|
//
|
|
// 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)
|
|
{
|
|
size = scalar_vec3(size);
|
|
orient_and_align(size, align=align, center=center, chain=true) {
|
|
if (orient == ORIENT_X) {
|
|
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(size, center=true);
|
|
back(masksize.y/2) cube(masksize, center=true);
|
|
}
|
|
}
|
|
} else if (orient == ORIENT_Y) {
|
|
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(size, center=true);
|
|
right(masksize.x/2) cube(masksize, center=true);
|
|
}
|
|
}
|
|
} else if (orient == ORIENT_Z) {
|
|
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(size, center=true);
|
|
back(masksize.y/2) cube(masksize, center=true);
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Section: Cylindroids
|
|
|
|
|
|
// Module: cyl()
|
|
//
|
|
// Description:
|
|
// Creates cylinders in various alignments and orientations,
|
|
// with optional fillets and chamfers. You can use `r` and `l`
|
|
// interchangably, and all variants allow specifying size
|
|
// by either `r`|`d`, or `r1`|`d1` and `r2`|`d2`.
|
|
// Note that that chamfers and fillets cannot cross the
|
|
// midpoint of the cylinder's length.
|
|
//
|
|
// Usage: Normal Cylinders
|
|
// cyl(l|h, r|d, [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r1|d1, r2/d2, [circum], [realign], [orient], [align], [center]);
|
|
//
|
|
// Usage: Chamferred Cylinders
|
|
// cyl(l|h, r|d, chamfer, [chamfang], [from_end], [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, chamfer1, [chamfang1], [from_end], [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, chamfer2, [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, chamfer1, chamfer2, [chamfang1], [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
|
|
//
|
|
// Usage: Rounded/Filleted Cylinders
|
|
// cyl(l|h, r|d, fillet, [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, fillet1, [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, fillet2, [circum], [realign], [orient], [align], [center]);
|
|
// cyl(l|h, r|d, fillet1, fillet2, [circum], [realign], [orient], [align], [center]);
|
|
//
|
|
// Arguments:
|
|
// l / h = Length of cylinder along oriented axis. (Default: 1.0)
|
|
// r = Radius of cylinder.
|
|
// r1 = Radius of the negative (X-, Y-, Z-) end of cylinder.
|
|
// r2 = Radius of the positive (X+, Y+, Z+) end of cylinder.
|
|
// d = Diameter of cylinder.
|
|
// d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder.
|
|
// d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder.
|
|
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
|
|
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
|
|
// chamfer1 = The size of the chamfer on the axis-negative end of the cylinder. Default: none.
|
|
// chamfer2 = The size of the chamfer on the axis-positive end of the cylinder. Default: none.
|
|
// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
|
|
// chamfang1 = The angle in degrees of the chamfer on the axis-negative end of the cylinder.
|
|
// chamfang2 = The angle in degrees of the chamfer on the axis-positive end of the cylinder.
|
|
// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
|
|
// fillet = The radius of the fillets on the ends of the cylinder. Default: none.
|
|
// fillet1 = The radius of the fillet on the axis-negative end of the cylinder.
|
|
// fillet2 = The radius of the fillet on the axis-positive end of the cylinder.
|
|
// 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`.
|
|
//
|
|
// Example: By Radius
|
|
// xdistribute(30) {
|
|
// cyl(l=40, r=10);
|
|
// cyl(l=40, r1=10, r2=5);
|
|
// }
|
|
//
|
|
// Example: By Diameter
|
|
// xdistribute(30) {
|
|
// cyl(l=40, d=25);
|
|
// cyl(l=40, d1=25, d2=10);
|
|
// }
|
|
//
|
|
// Example: Chamferring
|
|
// xdistribute(60) {
|
|
// // Shown Left to right.
|
|
// cyl(l=40, d=40, chamfer=7); // Default chamfang=45
|
|
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false);
|
|
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true);
|
|
// }
|
|
//
|
|
// Example: Rounding/Filleting
|
|
// cyl(l=40, d=40, fillet=10);
|
|
//
|
|
// Example: Heterogenous Chamfers and Fillets
|
|
// ydistribute(80) {
|
|
// // Shown Front to Back.
|
|
// cyl(l=40, d=40, fillet1=15, orient=ORIENT_X);
|
|
// cyl(l=40, d=40, chamfer2=5, orient=ORIENT_X);
|
|
// cyl(l=40, d=40, chamfer1=12, fillet2=10, orient=ORIENT_X);
|
|
// }
|
|
//
|
|
// Example: Putting it all together
|
|
// cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, fillet2=5);
|
|
module cyl(
|
|
l=undef, h=undef,
|
|
r=undef, r1=undef, r2=undef,
|
|
d=undef, d1=undef, d2=undef,
|
|
chamfer=undef, chamfer1=undef, chamfer2=undef,
|
|
chamfang=undef, chamfang1=undef, chamfang2=undef,
|
|
fillet=undef, fillet1=undef, fillet2=undef,
|
|
circum=false, realign=false, from_end=false,
|
|
orient=ORIENT_Z, align=CENTER, center=undef
|
|
) {
|
|
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;
|
|
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);
|
|
} else {
|
|
vang = atan2(l, r1-r2)/2;
|
|
chang1 = 90-first_defined([chamfang1, chamfang, vang]);
|
|
chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]);
|
|
cham1 = first_defined([chamfer1, chamfer]) * (from_end? 1 : tan(chang1));
|
|
cham2 = first_defined([chamfer2, chamfer]) * (from_end? 1 : tan(chang2));
|
|
fil1 = first_defined([fillet1, fillet]);
|
|
fil2 = first_defined([fillet2, fillet]);
|
|
if (chamfer != undef) {
|
|
assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.");
|
|
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
|
|
assert(chamfer <= l/2, "chamfer is larger than half the length of the cylinder.");
|
|
}
|
|
if (cham1 != undef) {
|
|
assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
|
|
assert(cham1 <= l/2, "chamfer1 is larger than half the length of the cylinder.");
|
|
}
|
|
if (cham2 != undef) {
|
|
assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
|
|
assert(cham2 <= l/2, "chamfer2 is larger than half the length of the cylinder.");
|
|
}
|
|
if (fillet != undef) {
|
|
assert(fillet <= r1, "fillet is larger than the r1 radius of the cylinder.");
|
|
assert(fillet <= r2, "fillet is larger than the r2 radius of the cylinder.");
|
|
assert(fillet <= l/2, "fillet is larger than half the length of the cylinder.");
|
|
}
|
|
if (fil1 != undef) {
|
|
assert(fil1 <= r1, "fillet1 is larger than the r1 radius of the cylinder.");
|
|
assert(fil1 <= l/2, "fillet1 is larger than half the length of the cylinder.");
|
|
}
|
|
if (fil2 != undef) {
|
|
assert(fil2 <= r2, "fillet2 is larger than the r1 radius of the cylinder.");
|
|
assert(fil2 <= l/2, "fillet2 is larger than half the length of the cylinder.");
|
|
}
|
|
|
|
dy1 = first_defined([cham1, fil1, 0]);
|
|
dy2 = first_defined([cham2, fil2, 0]);
|
|
maxd = max(r1,r2,l);
|
|
|
|
rotate_extrude(convexity=2) {
|
|
hull() {
|
|
difference() {
|
|
union() {
|
|
difference() {
|
|
back(l/2) {
|
|
if (cham2!=undef && cham2>0) {
|
|
rr2 = sc * (r2 + (r1-r2)*dy2/l);
|
|
chlen2 = min(rr2, cham2/sin(chang2));
|
|
translate([rr2,-cham2]) {
|
|
rotate(-chang2) {
|
|
translate([-chlen2,-chlen2]) {
|
|
square(chlen2, center=false);
|
|
}
|
|
}
|
|
}
|
|
} else if (fil2!=undef && fil2>0) {
|
|
translate([r2-fil2*tan(vang),-fil2]) {
|
|
circle(r=fil2);
|
|
}
|
|
} else {
|
|
translate([r2-0.005,-0.005]) {
|
|
square(0.01, center=true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the corner fiddly bits never cross the X axis.
|
|
fwd(maxd) square(maxd, center=false);
|
|
}
|
|
difference() {
|
|
fwd(l/2) {
|
|
if (cham1!=undef && cham1>0) {
|
|
rr1 = sc * (r1 + (r2-r1)*dy1/l);
|
|
chlen1 = min(rr1, cham1/sin(chang1));
|
|
translate([rr1,cham1]) {
|
|
rotate(chang1) {
|
|
left(chlen1) {
|
|
square(chlen1, center=false);
|
|
}
|
|
}
|
|
}
|
|
} else if (fil1!=undef && fil1>0) {
|
|
right(r1) {
|
|
translate([-fil1/tan(vang),fil1]) {
|
|
fsegs1 = quantup(segs(fil1),4);
|
|
circle(r=fil1,$fn=fsegs1);
|
|
}
|
|
}
|
|
} else {
|
|
right(r1-0.01) {
|
|
square(0.01, center=false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the corner fiddly bits never cross the X axis.
|
|
square(maxd, center=false);
|
|
}
|
|
|
|
// Force the hull to extend to the axis
|
|
right(0.01/2) square([0.01, l], center=true);
|
|
}
|
|
|
|
// Clear anything left of the Y axis.
|
|
left(maxd/2) square(maxd, center=true);
|
|
|
|
// Clear anything right of face
|
|
right((r1+r2)/2) {
|
|
rotate(90-vang*2) {
|
|
fwd(maxd/2) square(maxd, center=false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 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:
|
|
// Creates a cylinder oriented along the X axis.
|
|
//
|
|
// Usage:
|
|
// xcyl(l|h, r|d, [align|center]);
|
|
// xcyl(l|h, r1|d1, r2|d2, [align|center]);
|
|
//
|
|
// Arguments:
|
|
// l / h = Length of cylinder along oriented axis. (Default: `1.0`)
|
|
// r = Radius of cylinder.
|
|
// r1 = Optional radius of left (X-) end of cylinder.
|
|
// r2 = Optional radius of right (X+) end of cylinder.
|
|
// d = Optional diameter of cylinder. (use instead of `r`)
|
|
// 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`.
|
|
//
|
|
// Example: By Radius
|
|
// ydistribute(50) {
|
|
// xcyl(l=35, r=10);
|
|
// xcyl(l=35, r1=15, r2=5);
|
|
// }
|
|
//
|
|
// Example: By Diameter
|
|
// ydistribute(50) {
|
|
// xcyl(l=35, d=20);
|
|
// xcyl(l=35, d1=30, d2=10);
|
|
// }
|
|
module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=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) children();
|
|
}
|
|
|
|
|
|
|
|
// Module: ycyl()
|
|
//
|
|
// Description:
|
|
// Creates a cylinder oriented along the Y axis.
|
|
//
|
|
// Usage:
|
|
// ycyl(l|h, r|d, [align|center]);
|
|
// ycyl(l|h, r1|d1, r2|d2, [align|center]);
|
|
//
|
|
// Arguments:
|
|
// l / h = Length of cylinder along oriented axis. (Default: `1.0`)
|
|
// r = Radius of cylinder.
|
|
// r1 = Radius of front (Y-) end of cone.
|
|
// r2 = Radius of back (Y+) end of one.
|
|
// d = Diameter of cylinder.
|
|
// d1 = Diameter of front (Y-) end of one.
|
|
// d2 = Diameter of back (Y+) end of one.
|
|
// align = The side of the origin to align to. Use constants from `constants.scad`. Default: `CENTER`
|
|
// center = Overrides `align` if given. If true, `align=CENTER`, if false, `align=UP`.
|
|
//
|
|
// Example: By Radius
|
|
// xdistribute(50) {
|
|
// ycyl(l=35, r=10);
|
|
// ycyl(l=35, r1=15, r2=5);
|
|
// }
|
|
//
|
|
// Example: By Diameter
|
|
// xdistribute(50) {
|
|
// ycyl(l=35, d=20);
|
|
// ycyl(l=35, d1=30, d2=10);
|
|
// }
|
|
module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=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) children();
|
|
}
|
|
|
|
|
|
|
|
// Module: zcyl()
|
|
//
|
|
// Description:
|
|
// Creates a cylinder oriented along the Z axis.
|
|
//
|
|
// Usage:
|
|
// zcyl(l|h, r|d, [align|center]);
|
|
// zcyl(l|h, r1|d1, r2|d2, [align|center]);
|
|
//
|
|
// Arguments:
|
|
// l / h = Length of cylinder along oriented axis. (Default: 1.0)
|
|
// r = Radius of cylinder.
|
|
// r1 = Radius of front (Y-) end of cone.
|
|
// r2 = Radius of back (Y+) end of one.
|
|
// d = Diameter of cylinder.
|
|
// d1 = Diameter of front (Y-) end of one.
|
|
// d2 = Diameter of back (Y+) end of one.
|
|
// align = The side of the origin to align to. Use constants from `constants.scad`. Default: `CENTER`
|
|
// center = Overrides `align` if given. If true, `align=CENTER`, if false, `align=UP`.
|
|
//
|
|
// Example: By Radius
|
|
// xdistribute(50) {
|
|
// zcyl(l=35, r=10);
|
|
// zcyl(l=35, r1=15, r2=5);
|
|
// }
|
|
//
|
|
// Example: By Diameter
|
|
// xdistribute(50) {
|
|
// zcyl(l=35, d=20);
|
|
// zcyl(l=35, d1=30, d2=10);
|
|
// }
|
|
module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=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) children();
|
|
}
|
|
|
|
|
|
|
|
// Module: tube()
|
|
//
|
|
// Description:
|
|
// Makes a hollow tube with the given outer size and wall thickness.
|
|
//
|
|
// Usage:
|
|
// tube(h, ir|id, wall, [realign], [orient], [align]);
|
|
// tube(h, or|od, wall, [realign], [orient], [align]);
|
|
// tube(h, ir|id, or|od, [realign], [orient], [align]);
|
|
// tube(h, ir1|id1, ir2|id2, wall, [realign], [orient], [align]);
|
|
// tube(h, or1|od1, or2|od2, wall, [realign], [orient], [align]);
|
|
// tube(h, ir1|id1, ir2|id2, or1|od1, or2|od2, [realign], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// h = height of tube. (Default: 1)
|
|
// or = Outer radius of tube.
|
|
// or1 = Outer radius of bottom of tube. (Default: value of r)
|
|
// or2 = Outer radius of top of tube. (Default: value of r)
|
|
// od = Outer diameter of tube.
|
|
// od1 = Outer diameter of bottom of tube.
|
|
// od2 = Outer diameter of top of tube.
|
|
// wall = horizontal thickness of tube wall. (Default 0.5)
|
|
// ir = Inner radius of tube.
|
|
// ir1 = Inner radius of bottom of tube.
|
|
// ir2 = Inner radius of top of tube.
|
|
// id = Inner diameter of tube.
|
|
// id1 = Inner diameter of bottom of tube.
|
|
// id2 = Inner diameter of top of tube.
|
|
// realign = If true, rotate the tube by half the angle of one face.
|
|
// orient = Orientation of the tube. Use the `ORIENT_` constants from `constants.scad`. Default: vertical.
|
|
// align = Alignment of the tube. Use the constants from `constants.scad`. Default: centered.
|
|
//
|
|
// Example: These all Produce the Same Tube
|
|
// tube(h=30, or=40, wall=5);
|
|
// tube(h=30, ir=35, wall=5);
|
|
// tube(h=30, or=40, ir=35);
|
|
// tube(h=30, od=80, id=70);
|
|
// Example: These all Produce the Same Conical Tube
|
|
// tube(h=30, or1=40, or2=25, wall=5);
|
|
// tube(h=30, ir1=35, or2=20, wall=5);
|
|
// 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);
|
|
module tube(
|
|
h=1, wall=undef,
|
|
r=undef, r1=undef, r2=undef,
|
|
d=undef, d1=undef, d2=undef,
|
|
or=undef, or1=undef, or2=undef,
|
|
od=undef, od1=undef, od2=undef,
|
|
ir=undef, id=undef, ir1=undef,
|
|
ir2=undef, id1=undef, id2=undef,
|
|
center=undef, orient=ORIENT_Z, align=UP,
|
|
realign=false
|
|
) {
|
|
r1 = first_defined([or1, od1/2, r1, d1/2, or, od/2, r, d/2, ir1+wall, id1/2+wall, ir+wall, id/2+wall]);
|
|
r2 = first_defined([or2, od2/2, r2, d2/2, or, od/2, r, d/2, ir2+wall, id2/2+wall, ir+wall, id/2+wall]);
|
|
ir1 = first_defined([ir1, id1/2, ir, id/2, r1-wall, d1/2-wall, r-wall, d/2-wall]);
|
|
ir2 = first_defined([ir2, id2/2, ir, id/2, r2-wall, d2/2-wall, r-wall, d/2-wall]);
|
|
assert(ir1 <= r1, "Inner radius is larger than outer radius.");
|
|
assert(ir2 <= r2, "Inner radius is larger than outer radius.");
|
|
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) {
|
|
zrot(realign? 180/sides : 0) {
|
|
difference() {
|
|
cyl(h=h, r1=r1, r2=r2, $fn=sides) children();
|
|
cyl(h=h+0.05, r1=ir1, r2=ir2);
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: torus()
|
|
//
|
|
// Descriptiom:
|
|
// Creates a torus shape.
|
|
//
|
|
// Usage:
|
|
// torus(r|d, r2|d2, [orient], [align]);
|
|
// torus(or|od, ir|id, [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// r = major radius of torus ring. (use with of 'r2', or 'd2')
|
|
// r2 = minor radius of torus ring. (use with of 'r', or 'd')
|
|
// d = major diameter of torus ring. (use with of 'r2', or 'd2')
|
|
// d2 = minor diameter of torus ring. (use with of 'r', or 'd')
|
|
// or = outer radius of the torus. (use with 'ir', or 'id')
|
|
// ir = inside radius of the torus. (use with 'or', or 'od')
|
|
// od = outer diameter of the torus. (use with 'ir' or 'id')
|
|
// id = inside diameter of the torus. (use with 'or' or 'od')
|
|
// orient = Orientation of the torus. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
|
|
// align = Alignment of the torus. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example:
|
|
// // These all produce the same torus.
|
|
// torus(r=22.5, r2=7.5);
|
|
// torus(d=45, d2=15);
|
|
// torus(or=30, ir=15);
|
|
// torus(od=60, id=30);
|
|
module torus(
|
|
r=undef, d=undef,
|
|
r2=undef, d2=undef,
|
|
or=undef, od=undef,
|
|
ir=undef, id=undef,
|
|
orient=ORIENT_Z, align=CENTER, center=undef
|
|
) {
|
|
orr = get_radius(r=or, d=od, dflt=1.0);
|
|
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);
|
|
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();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 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 constants from `constants.scad`. Default: `CENTER`.
|
|
// Example:
|
|
// spheroid(d=100, circum=true, $fn=10);
|
|
module spheroid(r=undef, d=undef, circum=false, orient=UP, align=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:
|
|
// An alternate construction to the standard `sphere()` built-in, with different triangulation.
|
|
//
|
|
// Usage:
|
|
// staggered_sphere(r|d, [circum])
|
|
//
|
|
// Arguments:
|
|
// 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 constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example:
|
|
// staggered_sphere(d=100, circum=true, $fn=10);
|
|
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);
|
|
vsides = max(3, ceil(sides/2))+1;
|
|
step = 360/sides;
|
|
vstep = 180/(vsides-1);
|
|
rr = circum? (r / cos(180/sides) / cos(90/vsides)) : r;
|
|
pts = concat(
|
|
[[0,0,rr]],
|
|
[
|
|
for (p = [1:vsides-2], t = [0:sides-1]) let(
|
|
ta = (t+(p%2/2))*step,
|
|
pa = p*vstep
|
|
) spherical_to_xyz(rr, ta, pa)
|
|
],
|
|
[[0,0,-rr]]
|
|
);
|
|
pcnt = len(pts);
|
|
faces = concat(
|
|
[
|
|
for (i = [1:sides]) each [
|
|
[0, i%sides+1, i],
|
|
[pcnt-1, pcnt-1-(i%sides+1), pcnt-1-i]
|
|
]
|
|
],
|
|
[
|
|
for (p = [0:vsides-4], i = [0:sides-1]) let(
|
|
b1 = 1+p*sides,
|
|
b2 = 1+(p+1)*sides,
|
|
v1 = b1+i,
|
|
v2 = b1+(i+1)%sides,
|
|
v3 = b2+((i+((p%2)?(sides-1):0))%sides),
|
|
v4 = b2+((i+1+((p%2)?(sides-1):0))%sides)
|
|
) 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();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Section: 3D Printing Shapes
|
|
|
|
|
|
// Module: teardrop2d()
|
|
//
|
|
// Description:
|
|
// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes.
|
|
//
|
|
// Usage:
|
|
// teardrop2d(r|d, [ang], [cap_h]);
|
|
//
|
|
// Arguments:
|
|
// r = radius of circular part of teardrop. (Default: 1)
|
|
// d = diameter of spherical portion of bottom. (Use instead of r)
|
|
// ang = angle of hat walls from the Y axis. (Default: 45 degrees)
|
|
// cap_h = if given, height above center where the shape will be truncated.
|
|
//
|
|
// Example(2D): Typical Shape
|
|
// teardrop2d(r=30, ang=30);
|
|
// Example(2D): Crop Cap
|
|
// teardrop2d(r=30, ang=30, cap_h=40);
|
|
// Example(2D): Close Crop
|
|
// teardrop2d(r=30, ang=30, cap_h=20);
|
|
module teardrop2d(r=1, d=undef, ang=45, cap_h=undef)
|
|
{
|
|
eps = 0.01;
|
|
r = get_radius(r=r, d=d, dflt=1);
|
|
cord = 2 * r * cos(ang);
|
|
cord_h = r * sin(ang);
|
|
tip_y = (cord/2)/tan(ang);
|
|
cap_h = min((!is_undef(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h);
|
|
cap_w = cord * (1 - (cap_h - cord_h)/tip_y);
|
|
difference() {
|
|
hull() {
|
|
zrot(90) circle(r=r);
|
|
back(cap_h-eps/2) square([max(eps,cap_w), eps], center=true);
|
|
}
|
|
back(r+cap_h) square(2*r, center=true);
|
|
}
|
|
}
|
|
|
|
|
|
// Module: teardrop()
|
|
//
|
|
// Description:
|
|
// Makes a teardrop shape in the XZ plane. Useful for 3D printable holes.
|
|
//
|
|
// Usage:
|
|
// teardrop(r|d, l|h, [ang], [cap_h], [orient], [align])
|
|
//
|
|
// Arguments:
|
|
// r = Radius of circular part of teardrop. (Default: 1)
|
|
// d = Diameter of circular portion of bottom. (Use instead of r)
|
|
// l = Thickness of teardrop. (Default: 1)
|
|
// ang = Angle of hat walls from the Z axis. (Default: 45 degrees)
|
|
// cap_h = If given, height above center where the shape will be truncated.
|
|
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// teardrop(r=30, h=10, ang=30);
|
|
// Example: Crop Cap
|
|
// teardrop(r=30, h=10, ang=30, cap_h=40);
|
|
// Example: Close Crop
|
|
// teardrop(r=30, h=10, ang=30, cap_h=20);
|
|
module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
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) {
|
|
linear_extrude(height=l, center=true, slices=2) {
|
|
teardrop2d(r=r, ang=ang, cap_h=cap_h);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: onion()
|
|
//
|
|
// Description:
|
|
// Creates a sphere with a conical hat, to make a 3D teardrop.
|
|
//
|
|
// Usage:
|
|
// onion(r|d, [maxang], [cap_h], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// r = radius of spherical portion of the bottom. (Default: 1)
|
|
// d = diameter of spherical portion of bottom.
|
|
// cap_h = height above sphere center to truncate teardrop shape.
|
|
// maxang = angle of cone on top from vertical.
|
|
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// onion(r=30, maxang=30);
|
|
// Example: Crop Cap
|
|
// onion(r=30, maxang=30, cap_h=40);
|
|
// Example: Close Crop
|
|
// onion(r=30, maxang=30, cap_h=20);
|
|
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) {
|
|
rotate_extrude(convexity=2) {
|
|
difference() {
|
|
teardrop2d(r=r, ang=maxang, cap_h=h);
|
|
left(r) square(size=[2*r,maxd], center=true);
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: narrowing_strut()
|
|
//
|
|
// Description:
|
|
// Makes a rectangular strut with the top side narrowing in a triangle.
|
|
// The shape created may be likened to an extruded home plate from baseball.
|
|
// This is useful for constructing parts that minimize the need to support
|
|
// overhangs.
|
|
//
|
|
// Usage:
|
|
// narrowing_strut(w, l, wall, [ang], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// w = Width (thickness) of the strut.
|
|
// l = Length of the strut.
|
|
// wall = height of rectangular portion of the strut.
|
|
// ang = angle that the trianglar side will converge at.
|
|
// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example:
|
|
// narrowing_strut(w=10, l=100, wall=5, ang=30);
|
|
module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=UP)
|
|
{
|
|
h = wall + w/2/tan(ang);
|
|
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);
|
|
back(wall-0.001) {
|
|
yscale(1/tan(ang)) {
|
|
difference() {
|
|
zrot(45) square(w/sqrt(2), center=true);
|
|
fwd(w/2) square(w, center=true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: thinning_wall()
|
|
//
|
|
// Description:
|
|
// Makes a rectangular wall which thins to a smaller width in the center,
|
|
// with angled supports to prevent critical overhangs.
|
|
//
|
|
// Usage:
|
|
// thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// h = height of wall.
|
|
// l = length of wall. If given as a vector of two numbers, specifies bottom and top lengths, respectively.
|
|
// thick = thickness of wall.
|
|
// ang = maximum overhang angle of diagonal brace.
|
|
// strut = the width of the diagonal brace.
|
|
// wall = the thickness of the thinned portion of the wall.
|
|
// orient = Orientation of the length axis of the wall. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// 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_Z, align=CENTER)
|
|
{
|
|
l1 = (l[0] == undef)? l : l[0];
|
|
l2 = (l[1] == undef)? l : l[1];
|
|
|
|
trap_ang = atan2((l2-l1)/2, h);
|
|
corr1 = 1 + sin(trap_ang);
|
|
corr2 = 1 - sin(trap_ang);
|
|
|
|
z1 = h/2;
|
|
z2 = max(0.1, z1 - strut);
|
|
z3 = max(0.05, z2 - (thick-wall)/2*sin(90-ang)/sin(ang));
|
|
|
|
x1 = l2/2;
|
|
x2 = max(0.1, x1 - strut*corr1);
|
|
x3 = max(0.05, x2 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr1);
|
|
x4 = l1/2;
|
|
x5 = max(0.1, x4 - strut*corr2);
|
|
x6 = max(0.05, x5 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr2);
|
|
|
|
y1 = thick/2;
|
|
y2 = y1 - min(z2-z3, x2-x3) * sin(ang);
|
|
|
|
size = [l1, thick, h];
|
|
orient_and_align(size, orient, align, size2=[l2,thick], chain=true) {
|
|
polyhedron(
|
|
points=[
|
|
[-x4, -y1, -z1],
|
|
[ x4, -y1, -z1],
|
|
[ x1, -y1, z1],
|
|
[-x1, -y1, z1],
|
|
|
|
[-x5, -y1, -z2],
|
|
[ x5, -y1, -z2],
|
|
[ x2, -y1, z2],
|
|
[-x2, -y1, z2],
|
|
|
|
[-x6, -y2, -z3],
|
|
[ x6, -y2, -z3],
|
|
[ x3, -y2, z3],
|
|
[-x3, -y2, z3],
|
|
|
|
[-x4, y1, -z1],
|
|
[ x4, y1, -z1],
|
|
[ x1, y1, z1],
|
|
[-x1, y1, z1],
|
|
|
|
[-x5, y1, -z2],
|
|
[ x5, y1, -z2],
|
|
[ x2, y1, z2],
|
|
[-x2, y1, z2],
|
|
|
|
[-x6, y2, -z3],
|
|
[ x6, y2, -z3],
|
|
[ x3, y2, z3],
|
|
[-x3, y2, z3],
|
|
],
|
|
faces=[
|
|
[ 4, 5, 1],
|
|
[ 5, 6, 2],
|
|
[ 6, 7, 3],
|
|
[ 7, 4, 0],
|
|
|
|
[ 4, 1, 0],
|
|
[ 5, 2, 1],
|
|
[ 6, 3, 2],
|
|
[ 7, 0, 3],
|
|
|
|
[ 8, 9, 5],
|
|
[ 9, 10, 6],
|
|
[10, 11, 7],
|
|
[11, 8, 4],
|
|
|
|
[ 8, 5, 4],
|
|
[ 9, 6, 5],
|
|
[10, 7, 6],
|
|
[11, 4, 7],
|
|
|
|
[11, 10, 9],
|
|
[20, 21, 22],
|
|
|
|
[11, 9, 8],
|
|
[20, 22, 23],
|
|
|
|
[16, 17, 21],
|
|
[17, 18, 22],
|
|
[18, 19, 23],
|
|
[19, 16, 20],
|
|
|
|
[16, 21, 20],
|
|
[17, 22, 21],
|
|
[18, 23, 22],
|
|
[19, 20, 23],
|
|
|
|
[12, 13, 17],
|
|
[13, 14, 18],
|
|
[14, 15, 19],
|
|
[15, 12, 16],
|
|
|
|
[12, 17, 16],
|
|
[13, 18, 17],
|
|
[14, 19, 18],
|
|
[15, 16, 19],
|
|
|
|
[ 0, 1, 13],
|
|
[ 1, 2, 14],
|
|
[ 2, 3, 15],
|
|
[ 3, 0, 12],
|
|
|
|
[ 0, 13, 12],
|
|
[ 1, 14, 13],
|
|
[ 2, 15, 14],
|
|
[ 3, 12, 15],
|
|
],
|
|
convexity=6
|
|
);
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: braced_thinning_wall()
|
|
//
|
|
// Description:
|
|
// Makes a rectangular wall with cross-bracing, which thins to a smaller width in the center,
|
|
// with angled supports to prevent critical overhangs.
|
|
//
|
|
// Usage:
|
|
// braced_thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// h = height of wall.
|
|
// l = length of wall.
|
|
// thick = thickness of wall.
|
|
// ang = maximum overhang angle of diagonal brace.
|
|
// strut = the width of the diagonal brace.
|
|
// wall = the thickness of the thinned portion of the wall.
|
|
// orient = Orientation of the length axis of the wall. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// braced_thinning_wall(h=50, l=100, thick=5);
|
|
module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
dang = atan((h-2*strut)/(l-2*strut));
|
|
dlen = (h-2*strut)/sin(dang);
|
|
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);
|
|
intersection() {
|
|
cube(size=[thick, l, h], center=true);
|
|
xrot_copies([-dang,dang]) {
|
|
zspread(strut/2) {
|
|
scale([1,1,1.5]) yrot(45) {
|
|
cube(size=[thick/sqrt(2), dlen, thick/sqrt(2)], center=true);
|
|
}
|
|
}
|
|
cube(size=[thick, dlen, strut/2], center=true);
|
|
}
|
|
}
|
|
}
|
|
cube(size=[wall, l-0.1, h-0.1], center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: thinning_triangle()
|
|
//
|
|
// Description:
|
|
// Makes a triangular wall with thick edges, which thins to a smaller width in
|
|
// the center, with angled supports to prevent critical overhangs.
|
|
//
|
|
// Usage:
|
|
// thinning_triangle(h, l, thick, [ang], [strut], [wall], [diagonly], [orient], [align|center]);
|
|
//
|
|
// Arguments:
|
|
// h = height of wall.
|
|
// l = length of wall.
|
|
// thick = thickness of wall.
|
|
// ang = maximum overhang angle of diagonal brace.
|
|
// strut = the width of the diagonal brace.
|
|
// wall = the thickness of the thinned portion of the wall.
|
|
// diagonly = boolean, which denotes only the diagonal side (hypotenuse) should be thick.
|
|
// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
// center = If true, centers shape. If false, overrides `align` with `UP+BACK`.
|
|
//
|
|
// Example: Centered
|
|
// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=true);
|
|
// Example: All Braces
|
|
// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=false);
|
|
// Example: Diagonal Brace Only
|
|
// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, diagonly=true, center=false);
|
|
module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false, center=undef, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
dang = atan(h/l);
|
|
dlen = h/sin(dang);
|
|
size = [thick, l, h];
|
|
orient_and_align(size, orient, align, center=center, noncentered=UP+BACK, orig_orient=ORIENT_Y, chain=true) {
|
|
difference() {
|
|
union() {
|
|
if (!diagonly) {
|
|
translate([0, 0, -h/2])
|
|
narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
|
|
translate([0, -l/2, 0])
|
|
xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
|
|
}
|
|
intersection() {
|
|
cube(size=[thick, l, h], center=true);
|
|
xrot(-dang) yrot(180) {
|
|
narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang);
|
|
}
|
|
}
|
|
cube(size=[wall, l-0.1, h-0.1], center=true);
|
|
}
|
|
xrot(-dang) {
|
|
translate([0, 0, h/2]) {
|
|
cube(size=[thick+0.1, l*2, h], center=true);
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: sparse_strut()
|
|
//
|
|
// Description:
|
|
// Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce
|
|
// the need for support material in 3D printing.
|
|
//
|
|
// Usage:
|
|
// sparse_strut(h, l, thick, [strut], [maxang], [max_bridge], [orient], [align])
|
|
//
|
|
// Arguments:
|
|
// h = height of strut wall.
|
|
// l = length of strut wall.
|
|
// thick = thickness of strut wall.
|
|
// maxang = maximum overhang angle of cross-braces.
|
|
// max_bridge = maximum bridging distance between cross-braces.
|
|
// strut = the width of the cross-braces.
|
|
// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// sparse_strut(h=40, l=100, thick=3);
|
|
// Example: Thinner Strut
|
|
// sparse_strut(h=40, l=100, thick=3, strut=2);
|
|
// Example: Larger maxang
|
|
// sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45);
|
|
// Example: Longer max_bridge
|
|
// sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45, max_bridge=30);
|
|
module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
zoff = h/2 - strut/2;
|
|
yoff = l/2 - strut/2;
|
|
|
|
maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
|
|
maxz = 2 * maxhyp * cos(maxang);
|
|
|
|
zreps = ceil(2*zoff/maxz);
|
|
zstep = 2*zoff / zreps;
|
|
|
|
hyp = zstep/2 / cos(maxang);
|
|
maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
|
|
|
|
yreps = ceil(2*yoff/maxy);
|
|
ystep = 2*yoff / yreps;
|
|
|
|
ang = atan(ystep/zstep);
|
|
len = zstep / cos(ang);
|
|
|
|
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)
|
|
cube(size=[thick, strut, h], center=true);
|
|
yspread(ystep, n=yreps) {
|
|
zspread(zstep, n=zreps) {
|
|
xrot( ang) cube(size=[thick, strut, len], center=true);
|
|
xrot(-ang) cube(size=[thick, strut, len], center=true);
|
|
}
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: sparse_strut3d()
|
|
//
|
|
// Usage:
|
|
// sparse_strut3d(h, w, l, [thick], [maxang], [max_bridge], [strut], [orient], [align]);
|
|
//
|
|
// Description:
|
|
// Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce the
|
|
// need for support material in 3D printing.
|
|
//
|
|
// Arguments:
|
|
// h = Z size of strut.
|
|
// w = X size of strut.
|
|
// l = Y size of strut.
|
|
// thick = thickness of strut walls.
|
|
// maxang = maximum overhang angle of cross-braces.
|
|
// max_bridge = maximum bridging distance between cross-braces.
|
|
// strut = the width of the cross-braces.
|
|
// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// sparse_strut3d(h=30, w=30, l=100);
|
|
// Example: Thinner strut
|
|
// sparse_strut3d(h=30, w=30, l=100, strut=2);
|
|
// Example: Larger maxang
|
|
// sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50);
|
|
// Example: Smaller max_bridge
|
|
// sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50, max_bridge=20);
|
|
module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge=30, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
|
|
xoff = w - thick;
|
|
yoff = l - thick;
|
|
zoff = h - thick;
|
|
|
|
xreps = ceil(xoff/yoff);
|
|
yreps = ceil(yoff/xoff);
|
|
zreps = ceil(zoff/min(xoff, yoff));
|
|
|
|
xstep = xoff / xreps;
|
|
ystep = yoff / yreps;
|
|
zstep = zoff / zreps;
|
|
|
|
cross_ang = atan2(xstep, ystep);
|
|
cross_len = hypot(xstep, ystep);
|
|
|
|
supp_ang = min(maxang, min(atan2(max_bridge, zstep), atan2(cross_len/2, zstep)));
|
|
supp_reps = floor(cross_len/2/(zstep*sin(supp_ang)));
|
|
supp_step = cross_len/2/supp_reps;
|
|
|
|
size = [w, l, h];
|
|
orient_and_align(size, orient, align, orig_orient=ORIENT_Y, chain=true) {
|
|
intersection() {
|
|
union() {
|
|
ybridge = (l - (yreps+1) * strut) / yreps;
|
|
xspread(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=ybridge/ceil(ybridge/max_bridge));
|
|
yspread(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
|
|
for(zs = [0:zreps-1]) {
|
|
for(xs = [0:xreps-1]) {
|
|
for(ys = [0:yreps-1]) {
|
|
translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, (zs+0.5)*zstep-zoff/2]) {
|
|
zflip_copy(offset=-(zstep-strut)/2) {
|
|
xflip_copy() {
|
|
zrot(cross_ang) {
|
|
down(strut/2) {
|
|
cube([strut, cross_len, strut], center=true);
|
|
}
|
|
if (zreps>1) {
|
|
back(cross_len/2) {
|
|
zrot(-cross_ang) {
|
|
down(strut) upcube([strut, strut, zstep+strut]);
|
|
}
|
|
}
|
|
}
|
|
for (soff = [0 : supp_reps-1] ) {
|
|
yflip_copy() {
|
|
back(soff*supp_step) {
|
|
skew_xy(ya=supp_ang) {
|
|
upcube([strut, strut, zstep]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cube([w,l,h], center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: corrugated_wall()
|
|
//
|
|
// Description:
|
|
// Makes a corrugated wall which relieves contraction stress while still
|
|
// providing support strength. Designed with 3D printing in mind.
|
|
//
|
|
// Usage:
|
|
// corrugated_wall(h, l, thick, [strut], [wall], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// h = height of strut wall.
|
|
// l = length of strut wall.
|
|
// thick = thickness of strut wall.
|
|
// strut = the width of the cross-braces.
|
|
// wall = thickness of corrugations.
|
|
// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
|
|
// align = Alignment of the shape. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example: Typical Shape
|
|
// corrugated_wall(h=50, l=100);
|
|
// Example: Wider Strut
|
|
// corrugated_wall(h=50, l=100, strut=8);
|
|
// Example: Thicker Wall
|
|
// corrugated_wall(h=50, l=100, strut=8, wall=3);
|
|
module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, align=CENTER)
|
|
{
|
|
amplitude = (thick - wall) / 2;
|
|
period = min(15, thick * 2);
|
|
steps = quantup(segs(thick/2),4);
|
|
step = period/steps;
|
|
il = l - 2*strut + 2*step;
|
|
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(
|
|
[for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ],
|
|
[for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ]
|
|
)
|
|
);
|
|
}
|
|
difference() {
|
|
cube([thick, l, h], center=true);
|
|
cube([thick+0.5, l-2*strut, h-2*strut], center=true);
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Section: Miscellaneous
|
|
|
|
|
|
// Module: nil()
|
|
//
|
|
// Description:
|
|
// Useful when you MUST pass a child to a module, but you want it to be nothing.
|
|
module nil() union() {}
|
|
|
|
|
|
// Module: noop()
|
|
//
|
|
// 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: pie_slice()
|
|
//
|
|
// Description:
|
|
// Creates a pie slice shape.
|
|
//
|
|
// Usage:
|
|
// pie_slice(ang, l|h, r|d, [orient], [align|center]);
|
|
// pie_slice(ang, l|h, r1|d1, r2|d2, [orient], [align|center]);
|
|
//
|
|
// Arguments:
|
|
// ang = pie slice angle in degrees.
|
|
// h = height of pie slice.
|
|
// r = radius of pie slice.
|
|
// r1 = bottom radius of pie slice.
|
|
// r2 = top radius of pie slice.
|
|
// d = diameter of pie slice.
|
|
// d1 = bottom diameter of pie slice.
|
|
// d2 = top diameter of pie slice.
|
|
// orient = Orientation of the pie slice. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
|
|
// align = Alignment of the pie slice. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
// center = If given, overrides `align`. A true value sets `align=CENTER`, false sets `align=UP`.
|
|
//
|
|
// Example: Cylindrical Pie Slice
|
|
// pie_slice(ang=45, l=20, r=30);
|
|
// Example: Conical Pie Slice
|
|
// pie_slice(ang=60, l=20, d1=50, d2=70);
|
|
module pie_slice(
|
|
ang=30, l=undef,
|
|
r=10, r1=undef, r2=undef,
|
|
d=undef, d1=undef, d2=undef,
|
|
orient=ORIENT_Z, align=UP,
|
|
center=undef, h=undef
|
|
) {
|
|
l = first_defined([l, h, 1]);
|
|
r1 = get_radius(r1, r, d1, d, 10);
|
|
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) {
|
|
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);
|
|
difference() {
|
|
fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
|
|
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
// Module: interior_fillet()
|
|
//
|
|
// Description:
|
|
// Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
|
|
// Center this part along the concave edge to be chamferred and union it in.
|
|
//
|
|
// Usage:
|
|
// interior_fillet(l, r, [ang], [overlap], [orient], [align]);
|
|
//
|
|
// Arguments:
|
|
// l = length of edge to fillet.
|
|
// r = radius of fillet.
|
|
// ang = angle between faces to fillet.
|
|
// overlap = overlap size for unioning with faces.
|
|
// orient = Orientation of the fillet. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
|
|
// align = Alignment of the fillet. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
//
|
|
// Example:
|
|
// union() {
|
|
// translate([0,2,-4]) upcube([20, 4, 24]);
|
|
// translate([0,-10,-4]) upcube([20, 20, 4]);
|
|
// color("green") interior_fillet(l=20, r=10, orient=ORIENT_XNEG);
|
|
// }
|
|
//
|
|
// Example:
|
|
// 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=CENTER) {
|
|
dy = r/tan(ang/2);
|
|
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) {
|
|
translate([0,r/2,r/2]) cube([l,r,r], center=true);
|
|
} else {
|
|
rotate([90,0,90]) pie_slice(ang=ang, r=dy+overlap, h=l, center=true);
|
|
}
|
|
}
|
|
translate([0,dy,r]) xcyl(l=l+0.1, r=r);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Module: slot()
|
|
//
|
|
// Description:
|
|
// Makes a linear slot with rounded ends, appropriate for bolts to slide along.
|
|
//
|
|
// Usage:
|
|
// slot(h, l, r|d, [orient], [align|center]);
|
|
// slot(h, p1, p2, r|d, [orient], [align|center]);
|
|
// slot(h, l, r1|d1, r2|d2, [orient], [align|center]);
|
|
// slot(h, p1, p2, r1|d1, r2|d2, [orient], [align|center]);
|
|
//
|
|
// Arguments:
|
|
// p1 = center of starting circle of slot.
|
|
// p2 = center of ending circle of slot.
|
|
// l = length of slot along the X axis.
|
|
// h = height of slot shape. (default: 10)
|
|
// r = radius of slot circle. (default: 5)
|
|
// r1 = bottom radius of slot cone.
|
|
// r2 = top radius of slot cone.
|
|
// d = diameter of slot circle.
|
|
// d1 = bottom diameter of slot cone.
|
|
// d2 = top diameter of slot cone.
|
|
//
|
|
// Example: Between Two Points
|
|
// slot([0,0,0], [50,50,0], r1=5, r2=10, h=5);
|
|
// Example: By Length
|
|
// slot(l=50, r1=5, r2=10, h=5);
|
|
module slot(
|
|
p1=undef, p2=undef, h=10, l=undef,
|
|
r=undef, r1=undef, r2=undef,
|
|
d=undef, d1=undef, d2=undef
|
|
) {
|
|
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);
|
|
}
|
|
|
|
|
|
// Module: arced_slot()
|
|
//
|
|
// Description:
|
|
// Makes an arced slot, appropriate for bolts to slide along.
|
|
//
|
|
// Usage:
|
|
// arced_slot(h, r|d, sr|sd, [sa], [ea], [orient], [align|center], [$fn2]);
|
|
// arced_slot(h, r|d, sr1|sd1, sr2|sd2, [sa], [ea], [orient], [align|center], [$fn2]);
|
|
//
|
|
// Arguments:
|
|
// cp = centerpoint of slot arc. (default: [0, 0, 0])
|
|
// h = height of slot arc shape. (default: 1.0)
|
|
// r = radius of slot arc. (default: 0.5)
|
|
// d = diameter of slot arc. (default: 1.0)
|
|
// sr = radius of slot channel. (default: 0.5)
|
|
// sd = diameter of slot channel. (default: 0.5)
|
|
// sr1 = bottom radius of slot channel cone. (use instead of sr)
|
|
// sr2 = top radius of slot channel cone. (use instead of sr)
|
|
// sd1 = bottom diameter of slot channel cone. (use instead of sd)
|
|
// sd2 = top diameter of slot channel cone. (use instead of sd)
|
|
// sa = starting angle. (Default: 0.0)
|
|
// ea = ending angle. (Default: 90.0)
|
|
// orient = Orientation of the arced slot. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
|
|
// align = Alignment of the arced slot. Use the constants from `constants.scad`. Default: `CENTER`.
|
|
// center = If true, centers vertically. If false, drops flush with XY plane. Overrides `align`.
|
|
// $fn2 = The $fn value to use on the small round endcaps. The major arcs are still based on $fn. Default: $fn
|
|
//
|
|
// Example: Typical Arced Slot
|
|
// arced_slot(d=60, h=5, sd=10, sa=60, ea=280);
|
|
// Example: Conical Arced Slot
|
|
// arced_slot(r=60, h=5, sd1=10, sd2=15, sa=45, ea=180);
|
|
module arced_slot(
|
|
r=undef, d=undef, h=1.0,
|
|
sr=undef, sr1=undef, sr2=undef,
|
|
sd=undef, sd1=undef, sd2=undef,
|
|
sa=0, ea=90, cp=[0,0,0],
|
|
orient=ORIENT_Z, align=CENTER,
|
|
$fn2 = undef
|
|
) {
|
|
r = get_radius(r=r, d=d, dflt=2);
|
|
sr1 = get_radius(sr1, sr, sd1, sd, 2);
|
|
sr2 = get_radius(sr2, sr, sd2, sd, 2);
|
|
fn_minor = first_defined([$fn2, $fn]);
|
|
da = ea - sa;
|
|
size = [r+sr1, r+sr1, h];
|
|
orient_and_align(size, orient, align, chain=true) {
|
|
translate(cp) {
|
|
zrot(sa) {
|
|
difference() {
|
|
pie_slice(ang=da, l=h, r1=r+sr1, r2=r+sr2, orient=ORIENT_Z, align=CENTER);
|
|
cylinder(h=h+0.1, r1=r-sr1, r2=r-sr2, center=true);
|
|
}
|
|
right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
|
|
zrot(da) right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
|
|
}
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|