Massive reworking of documentation production.

This commit is contained in:
Revar Desmera 2019-03-22 21:13:18 -07:00
parent 045e5bafe6
commit 9a3c25d3d3
26 changed files with 8977 additions and 3257 deletions

View file

@ -14,13 +14,17 @@ For purposes of the BOSL library, the following terms apply:
- **Bottom**/**Down**/**Below**: Towards Z-
- **Top**/**Up**/**Above**: Towards Z+
- **Axis-Positive**: Towards the negative end of the axis the object is oriented on. IE: X-, Y-, or Z-.
- **Axis-Positive**: Towards the positive end of the axis the object is oriented on. IE: X+, Y+, or Z+.
- **Axis-Negative**: Towards the negative end of the axis the object is oriented on. IE: X-, Y-, or Z-.
- **Fillet**: A rounding of an interior or exterior edge.
- **Chamfer**: A bevelling of an interior or exterior edge.
- **Orientation**: The axis a part should be oriented to.
- **Alignment**: The side of the origin that the part should be on. Given as a vector.
## Common Arguments:
Args | What it is
------- | ----------------------------------------
fillet | Radius of rounding for interior or exterior edges.
chamfer | Size of chamfers/bevels for interior or exterior edges.
orient | Axis a part should be oriented along. Given as an XYZ triplet of rotation angles. It is recommended that you use the `ORIENT_` constants from `constants.scad`. Default is usually `ORIENT_Z` for vertical orientation.
align | Side of the origin that the part should be on. Given as a vector away from the origin. It is recommended that you use the `V_` constants from `constants.scad`. Default is usually `V_ZERO` for centered.
## Examples

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Bezier functions and modules.
// LibFile: beziers.scad
// Bezier functions and modules.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/beziers.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,12 +37,56 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <math.scad>
include <paths.scad>
include <transforms.scad>
include <constants.scad>
use <math.scad>
use <paths.scad>
use <transforms.scad>
// Formulae to calculate points on an N-point bezier curve.
// Section: Terminology
// **Polyline**: A series of points joined by straight line segements.
//
// **Bezier Curve**: A mathematical curve that joins two endpoints, following a curve determined by one or more control points.
//
// **Endpoint**: A point that is on the end of a bezier segment. This point lies on the bezier curve.
//
// **Control Point**: A point that influences the shape of the curve that connects two endpoints. This is often *NOT* on the bezier curve.
//
// **Degree**: The number of control points, plus one endpoint, needed to specify a bezier segment. Most beziers are cubic (degree 3).
//
// **Bezier Segment**: A list consisting of an endpoint, one or more control points, and a final endpoint. The number of control points is one less than the degree of the bezier. A cubic (degree 3) bezier segment looks something like:
// `[endpt1, cp1, cp2, endpt2]`
//
// **Bezier Path**: A list of bezier segments flattened out into a list of points, where each segment shares the endpoint of the previous segment as a start point. A cubic Bezier Path looks something like:
// `[endpt1, cp1, cp2, endpt2, cp3, cp4, endpt3]`
// **NOTE**: A bezier path is *NOT* a polyline. It is only the points and controls used to define the curve.
//
// **Spline Steps**: The number of straight-line segments to split a bezier segment into, to approximate the bezier curve. The more spline steps, the closer the approximation will be to the curve, but the slower it will be to generate. Usually defaults to 16.
// Section: Functions
// Function: bez_point()
// Usage:
// bez_point(curve, u)
// Description:
// Formula to calculate points on a bezier curve. The degree of
// the curve, N, is one less than the number of points in `curve`.
// Arguments:
// curve = The list of endpoints and control points for this bezier segment.
// u = The proportion of the way along the curve to find the point of. 0<=`u`<=1
// Example(2D): Quadratic (Degree 2) Bezier.
// bez = [[0,0], [30,30], [80,0]];
// trace_bezier(bez, N=len(bez)-1);
// translate(bez_point(bez, 0.3)) color("red") sphere(1);
// Example(2D): Cubic (Degree 3) Bezier
// bez = [[0,0], [5,35], [60,-25], [80,0]];
// trace_bezier(bez, N=len(bez)-1);
// translate(bez_point(bez, 0.4)) color("red") sphere(1);
// Example(2D): Degree 4 Bezier.
// bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]];
// trace_bezier(bez, N=len(bez)-1);
// translate(bez_point(bez, 0.8)) color("red") sphere(1);
function bez_point(curve,u)=
(len(curve) <= 1) ?
curve[0] :
@ -45,7 +95,25 @@ function bez_point(curve,u)=
u
);
// Takes an array of bezier points and converts it into a 3D polyline.
// Function: bezier_polyline()
// Usage:
// bezier_polyline(bezier, [splinesteps], [N])
// Description:
// Takes a bezier path and converts it into a polyline.
// Arguments:
// bezier = A bezier path to approximate.
// splinesteps = Number of straight lines to split each bezier segment into. default=16
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// Example(2D):
// bez = [
// [0,0], [-5,30],
// [20,60], [50,50], [110,30],
// [60,25], [70,0], [80,-25],
// [80,-50], [50,-50]
// ];
// trace_polyline(bez, size=1, N=3, showpts=true);
// trace_polyline(bezier_polyline(bez, N=3), size=3);
function bezier_polyline(bezier, splinesteps=16, N=3) = concat(
[
for (
@ -60,93 +128,86 @@ function bezier_polyline(bezier, splinesteps=16, N=3) = concat(
);
// Takes a closed 2D bezier path, and creates a 2D polygon from it.
// bezier = array of 2D bezier path points
// splinesteps = number of straight lines to split each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
module bezier_polygon(bezier, splinesteps=16, N=3) {
polypoints=bezier_polyline(bezier, splinesteps, N);
polygon(points=slice(polypoints, 0, -1));
}
// Generate bezier curve to fillet 2 line segments between 3 points.
// Returns two path points with surrounding cubic (N=3) bezier control points.
function fillet3pts(p0, p1, p2, r) = let(
// Function: fillet3pts()
// Usage:
// fillet3pts(p0, p1, p2, r);
// Description:
// Takes three points, defining two line segments, and works out the
// cubic (degree 3) bezier segment (and surrounding control points)
// needed to approximate a rounding of the corner with radius `r`.
// If there isn't room for a radius `r` rounding, uses the largest
// radius that will fit. Returns [cp1, endpt1, cp2, cp3, endpt2, cp4]
// Arguments:
// p0 = The starting point.
// p1 = The middle point.
// p2 = The ending point.
// r = The radius of the fillet/rounding.
// maxerr = Max amount bezier curve should diverge from actual radius curve. Default: 0.1
// Example(2D):
// p0 = [40, 0];
// p1 = [0, 0];
// p2 = [30, 30];
// trace_polyline([p0,p1,p2], showpts=true, size=0.5, color="green");
// fbez = fillet3pts(p0,p1,p2, 10);
// trace_bezier(slice(fbez, 1, -2), size=1);
function fillet3pts(p0, p1, p2, r, maxerr=0.1, w=0.5, dw=0.25) = let(
v0 = normalize(p0-p1),
v1 = normalize(p2-p1),
midv = normalize((v0+v1)/2),
a = vector3d_angle(v0,v1),
mr = min(distance(p0,p1), distance(p2,p1))*0.9,
tr = min(r/tan(a/2), mr),
tp0 = p1+v0*tr,
tp1 = p1+v1*tr,
w=-2.7e-5*a*a + 8.5e-3*a - 3e-3,
nw=max(0, w),
cp0 = tp0+nw*(p1-tp0),
cp1 = tp1+nw*(p1-tp1)
) [tp0, tp0, cp0, cp1, tp1, tp1];
tanr = min(r/tan(a/2), norm(p0-p1)*0.99, norm(p2-p1)*0.99),
tp0 = p1+v0*tanr,
tp1 = p1+v1*tanr,
cp = p1 + midv * tanr / cos(a/2),
cp0 = lerp(tp0, p1, w),
cp1 = lerp(tp1, p1, w),
cpr = norm(cp-tp0),
bp = bez_point([tp0, cp0, cp1, tp1], 0.5),
tdist = norm(cp-bp)
) (abs(tdist-cpr) <= maxerr)? [tp0, tp0, cp0, cp1, tp1, tp1] :
(tdist<cpr)? fillet3pts(p0, p1, p2, r, maxerr=maxerr, w=w+dw, dw=dw/2) :
fillet3pts(p0, p1, p2, r, maxerr=maxerr, w=w-dw, dw=dw/2);
// Takes a 3D polyline path and fillets the corners, returning a 3d cubic (N=3) bezier path.
function fillet_path(pts, fillet) = concat(
// Function: fillet_path()
// Usage:
// fillet_path(pts, fillet, [maxerr]);
// Description:
// Takes a 3D polyline path and fillets the corners, returning a 3d cubic (degree 3) bezier path.
// Arguments:
// pts = 3D Polyline path to fillet.
// fillet = The radius to fillet/round the polyline corners by.
// maxerr = Max amount bezier curve should diverge from actual radius curve. Default: 0.1
// Example(2D):
// pline = [[40,0], [0,0], [35,35], [0,70], [-10,60], [-5,55], [0,60]];
// bez = fillet_path(pline, 10);
// trace_polyline(pline, showpts=true, size=0.5, color="green");
// trace_bezier(bez, size=1);
function fillet_path(pts, fillet, maxerr=0.1) = concat(
[pts[0], pts[0]],
(len(pts) < 3)? [] : [
for (
p = [1 : len(pts)-2],
pt = fillet3pts(pts[p-1], pts[p], pts[p+1], fillet)
) pt
for (p = [1 : len(pts)-2]) let(
p1 = pts[p],
p0 = (pts[p-1]+p1)/2,
p2 = (pts[p+1]+p1)/2
) for (pt = fillet3pts(p0, p1, p2, fillet, maxerr=maxerr)) pt
],
[pts[len(pts)-1], pts[len(pts)-1]]
);
// Takes a closed 2D bezier and rotates it around the X axis, forming a solid.
// bezier = array of 2D points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = degrees of sweep to make. default=360
// Example:
// path = [
// [ 0, 10], [ 50, 0], [ 50, 40],
// [ 95, 40], [100, 40], [100, 45],
// [ 95, 45], [ 66, 45], [ 0, 20],
// [ 0, 12], [ 0, 12], [ 0, 10],
// [ 0, 10]
// ];
// revolve_bezier(path, splinesteps=32, $fn=180);
module revolve_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360) {
yrot(90) rotate_extrude(convexity=convexity, angle=angle) {
xrot(180) zrot(-90) bezier_polygon(bezier, splinesteps, N);
}
}
// Takes a closed 2D bezier and rotates it around the Z axis, forming a solid.
// Behaves like rotate_extrude(), except for beziers instead of shapes.
// bezier = array of 2D points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = degrees of sweep to make. default=360
// Example:
// path = [
// [ 0, 10], [ 50, 0], [ 50, 40],
// [ 95, 40], [100, 40], [100, 45],
// [ 95, 45], [ 66, 45], [ 0, 20],
// [ 0, 12], [ 0, 12], [ 0, 10],
// [ 0, 10]
// ];
// rotate_extrude_bezier(path, splinesteps=32, $fn=180);
module rotate_extrude_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360) {
rotate_extrude(convexity=convexity, angle=angle) {
bezier_polygon(bezier, splinesteps, N);
}
}
// Takes a 2D bezier path and closes it to the X axis.
// Function: bezier_close_to_axis()
// Usage:
// bezier_close_to_axis(bezier, [N]);
// Description:
// Takes a 2D bezier path and closes it to the X axis.
// Arguments:
// bezier = The 2D bezier path to close to the X axis.
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// Example(2D):
// bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
// closed = bezier_close_to_axis(bez);
// trace_bezier(closed, size=1);
function bezier_close_to_axis(bezier, N=3) =
let(
bezend = len(bezier)-1
@ -158,8 +219,20 @@ function bezier_close_to_axis(bezier, N=3) =
);
// Takes a bezier curve and closes it with a matching path that is
// lowered by a given amount towards the X axis.
// Function: bezier_offset()
// Usage:
// bezier_offset(inset, bezier, [N]);
// Description:
// Takes a 2D bezier path and closes it with a matching path that is
// lowered by a given amount towards the X axis.
// Arguments:
// inset = Amount to lower second path by.
// bezier = The 2D bezier path to close to the X axis.
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// Example(2D):
// bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
// closed = bezier_offset(5, bez);
// trace_bezier(closed, size=1);
function bezier_offset(inset, bezier, N=3) =
let(
backbez = reverse([ for (pt = bezier) [pt[0], pt[1]-inset] ]),
@ -172,56 +245,182 @@ function bezier_offset(inset, bezier, N=3) =
);
// Takes a 2D bezier and rotates it around the X axis, forming a solid.
// Section: Modules
// Module: bezier_polygon()
// Usage:
// bezier_polygon(bezier, [splinesteps], [N]) {
// Description:
// Takes a closed 2D bezier path, and creates a 2D polygon from it.
// Arguments:
// bezier = The closed bezier path to make into a polygon.
// splinesteps = Number of straight lines to split each bezier segment into. default=16
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// Example(2D):
// bez = [
// [0,0], [-5,30],
// [20,60], [50,50], [110,30],
// [60,25], [70,0], [80,-25],
// [80,-50], [50,-50], [30,-50],
// [5,-30], [0,0]
// ];
// trace_bezier(bez, N=3, size=3);
// linear_extrude(height=0.1) bezier_polygon(bez, N=3);
module bezier_polygon(bezier, splinesteps=16, N=3) {
polypoints=bezier_polyline(bezier, splinesteps, N);
polygon(points=slice(polypoints, 0, -1));
}
// Module: revolve_bezier()
// Usage:
// revolve_bezier(bezier, [splinesteps], [N], [convexity], [angle], [orient], [align])
// Description:
// Takes a closed 2D bezier and rotates it around the X axis, forming a solid.
// Arguments:
// bezier = array of 2D points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = Degrees of sweep to make. Default: 360
// orient = Orientation of the extrusion. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the extrusion. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example(FlatSpin):
// path = [
// [ 0, 10], [ 50, 0], [ 50, 40],
// [ 95, 40], [100, 40], [100, 45],
// [ 95, 45], [ 66, 45], [ 0, 20],
// [ 0, 12], [ 0, 12], [ 0, 10],
// [ 0, 10]
// ];
// revolve_bezier(path, splinesteps=32, $fn=180);
module revolve_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER)
{
maxx = max([for (pt = bezier) abs(pt[0])]);
maxy = max([for (pt = bezier) abs(pt[1])]);
orient_and_align([maxx*2,maxx*2,maxy*2], orient, align) {
rotate_extrude(convexity=convexity, angle=angle) {
xrot(180) zrot(-90) bezier_polygon(bezier, splinesteps, N);
}
}
}
// Module: rotate_extrude_bezier()
// Usage:
// rotate_extrude_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360)
// Description:
// Takes a closed 2D bezier and rotates it around the Z axis, forming a solid.
// Behaves like rotate_extrude(), except for beziers instead of shapes.
// Arguments:
// bezier = array of 2D points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = Degrees of sweep to make. Default: 360
// orient = Orientation of the extrusion. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the extrusion. Use the `V_` constants from `constants.scad`. Default: `ALIGN_POS`.
// Example(Spin):
// path = [
// [ 0, 10], [ 50, 0], [ 50, 40],
// [ 95, 40], [100, 40], [100, 45],
// [ 95, 45], [ 66, 45], [ 0, 20],
// [ 0, 12], [ 0, 12], [ 0, 10],
// [ 0, 10]
// ];
// rotate_extrude_bezier(path, splinesteps=32, $fn=180);
module rotate_extrude_bezier(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_Z, align=ALIGN_POS)
{
maxx = max([for (pt = bezier) abs(pt[0])]);
maxy = max([for (pt = bezier) abs(pt[1])]);
orient_and_align([maxx*2,maxx*2,maxy*2], orient, align) {
rotate_extrude(convexity=convexity, angle=angle) {
bezier_polygon(bezier, splinesteps, N);
}
}
}
// Module: revolve_bezier_solid_to_axis()
// Usage:
// revolve_bezier_solid_to_axis(bezier, [splinesteps], [N], [convexity], [angle], [orient], [align]);
// Description:
// Takes a 2D bezier and rotates it around the X axis, forming a solid.
// Arguments:
// bezier = array of points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = degrees of sweep to make. default=360
// Example:
// angle = Degrees of sweep to make. Default: 360
// orient = Orientation of the extrusion. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the extrusion. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example(FlatSpin):
// path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
// revolve_bezier_solid_to_axis(path, splinesteps=32, $fn=72);
module revolve_bezier_solid_to_axis(bezier, splinesteps=16, N=3, convexity=10, angle=360) {
revolve_bezier(bezier=bezier_close_to_axis(bezier), splinesteps=splinesteps, N=N, convexity=convexity, angle=angle);
module revolve_bezier_solid_to_axis(bezier, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER) {
revolve_bezier(bezier=bezier_close_to_axis(bezier), splinesteps=splinesteps, N=N, convexity=convexity, angle=angle, orient=orient, align=align);
}
// Takes a 2D bezier and rotates it around the X axis, into a hollow shell.
// Module: revolve_bezier_offset_shell()
// Usage:
// revolve_bezier_offset_shell(bezier, offset, [splinesteps], [N], [convexity], [angle], [orient], [align]);
// Description:
// Takes a 2D bezier and rotates it around the X axis, into a hollow shell.
// Arguments:
// bezier = array of points for the bezier path to rotate.
// offset = the thickness of the created shell.
// splinesteps = number of segments to divide each bezier segment into. default=16
// N = number of points in each bezier segment. default=3 (cubic)
// convexity = max number of walls a line could pass through, for preview. default=10
// angle = degrees of sweep to make. default=360
// Example:
// angle = degrees of sweep to make. Default: 360
// orient = Orientation of the extrusion. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the extrusion. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example(FlatSpin):
// path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
// revolve_bezier_offset_shell(path, offset=1, splinesteps=32, $fn=72);
module revolve_bezier_offset_shell(bezier, offset=1, splinesteps=16, N=3, convexity=10, angle=360) {
revolve_bezier(bezier=bezier_offset(offset, bezier), splinesteps=splinesteps, N=N);
module revolve_bezier_offset_shell(bezier, offset=1, splinesteps=16, N=3, convexity=10, angle=360, orient=ORIENT_X, align=V_CENTER) {
revolve_bezier(bezier=bezier_offset(offset, bezier), splinesteps=splinesteps, N=N, orient=orient, align=align);
}
// Extrudes 2D children along a bezier path.
// Module: extrude_2d_shapes_along_bezier()
// Usage:
// extrude_2d_shapes_along_bezier(bezier, [splinesteps], [N], [convexity], [clipsize]) ...
// Description:
// Extrudes 2D children along a bezier path.
// Arguments:
// bezier = array of points for the bezier path to extrude along.
// splinesteps = number of segments to divide each bezier segment into. default=16
// Example:
// Example(FR,FlatSpin):
// path = [ [0, 0, 0], [33, 33, 33], [66, -33, -33], [100, 0, 0] ];
// extrude_2d_shapes_along_bezier(path) difference(){circle(r=10); left(10/2) circle(r=8);}
// extrude_2d_shapes_along_bezier(path) difference(){
// circle(r=10);
// fwd(10/2) circle(r=8);
// }
module extrude_2d_shapes_along_bezier(bezier, splinesteps=16, N=3, convexity=10, clipsize=1000) {
path = slice(bezier_polyline(bezier, splinesteps, N), 0, -1);
extrude_2d_shapes_along_3dpath(path, convexity=convexity, clipsize=clipsize) children();
}
// Takes a closed 2D bezier path, centered on the XY plane, and
// extrudes it perpendicularly along a 3D bezier path, forming a solid.
// Module: extrude_bezier_along_bezier()
// Usage:
// extrude_bezier_along_bezier(bezier, path, [pathsteps], [bezsteps], [bezN], [pathN]);
// Description:
// Takes a closed 2D bezier path, centered on the XY plane, and
// extrudes it perpendicularly along a 3D bezier path, forming a solid.
// Arguments:
// bezier = Array of 2D points of a bezier path, to be extruded.
// path = Array of 3D points of a bezier path, to extrude along.
// pathsteps = number of steps to divide each path segment into.
// bezsteps = number of steps to divide each bezier segment into.
// bezN = number of points in each extruded bezier segment. default=3 (cubic)
// pathN = number of points in each path bezier segment. default=3 (cubic)
// Example:
// Example(FlatSpin):
// bez = [
// [-10, 0], [-15, -5],
// [ -5, -10], [ 0, -10], [ 5, -10],
@ -229,7 +428,7 @@ module extrude_2d_shapes_along_bezier(bezier, splinesteps=16, N=3, convexity=10,
// [ 5, 10], [ 0, 10], [-5, 10],
// [ 25, -15], [-10, 0]
// ];
// path = [ [0, 0, 0], [33, 33, 33], [66, -33, -33], [100, 0, 0] ];
// path = [ [0, 0, 0], [33, 33, 33], [90, 33, -33], [100, 0, 0] ];
// extrude_bezier_along_bezier(bez, path, pathsteps=32, bezsteps=16);
module extrude_bezier_along_bezier(bezier, path, pathsteps=16, bezsteps=16, bezN=3, pathN=3) {
bez_points = simplify2d_path(bezier_polyline(bezier, bezsteps, bezN));
@ -239,16 +438,23 @@ module extrude_bezier_along_bezier(bezier, path, pathsteps=16, bezsteps=16, bezN
// Takes a closed 2D bezier path, centered on the XY plane, and
// extrudes it linearly upwards, forming a solid.
// Module: linear_extrude_bezier()
// Usage:
// linear_extrude_bezier(bezier, height, [splinesteps], [N], [center], [convexity], [twist], [slices], [scale], [orient], [align]);
// Description:
// Takes a closed 2D bezier path, centered on the XY plane, and
// extrudes it linearly upwards, forming a solid.
// Arguments:
// bezier = Array of 2D points of a bezier path, to be extruded.
// splinesteps = number of steps to divide each bezier segment into. default=16
// N = number of points in each extruded bezier segment. default = 3 (cubic)
// center = if true, the extruded solid is centered vertically at z=0.
// splinesteps = Number of steps to divide each bezier segment into. default=16
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
// convexity = max number of walls a line could pass through, for preview. default=10
// twist = degrees to twist over length of extrusion. default=0
// scale = relative size of top of extrusion to the bottom. default=1.0
// slices = number of vertical slices to use for twisted extrusion. default=20
// twist = Angle in degrees to twist over the length of extrusion. default=0
// scale = Relative size of top of extrusion to the bottom. default=1.0
// slices = Number of vertical slices to use for twisted extrusion. default=20
// center = If true, the extruded solid is centered vertically at z=0.
// orient = Orientation of the extrusion. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the extrusion. Use the `V_` constants from `constants.scad`. Default: `ALIGN_POS`.
// Example:
// bez = [
// [-10, 0], [-15, -5],
@ -258,11 +464,37 @@ module extrude_bezier_along_bezier(bezier, path, pathsteps=16, bezsteps=16, bezN
// [ 25, -15], [-10, 0]
// ];
// linear_extrude_bezier(bez, height=20, splinesteps=32);
module linear_extrude_bezier(bezier, height=100, splinesteps=16, N=3, center=true, convexity=undef, twist=undef, slices=undef, scale=undef) {
linear_extrude(height=height, center=center, convexity=convexity, twist=twist, slices=slices, scale=scale) {
bezier_polygon(bezier, splinesteps=splinesteps, N=N);
module linear_extrude_bezier(bezier, height=100, splinesteps=16, N=3, center=undef, convexity=undef, twist=undef, slices=undef, scale=undef, orient=ORIENT_Z, align=ALIGN_POS) {
maxx = max([for (pt = bezier) abs(pt[0])]);
maxy = max([for (pt = bezier) abs(pt[1])]);
orient_and_align([maxx*2,maxy*2,height], orient, align) {
linear_extrude(height=height, center=true, convexity=convexity, twist=twist, slices=slices, scale=scale) {
bezier_polygon(bezier, splinesteps=splinesteps, N=N);
}
}
}
// Module: trace_bezier()
// Description:
// Renders 2D or 3D bezier paths and their associated control points.
// Useful for debugging bezier paths.
// Arguments:
// bez = the array of points in the bezier.
// N = Mark the first and every Nth vertex after in a different color and shape.
// size = diameter of the lines drawn.
// Example(2D):
// bez = [
// [-10, 0], [-15, -5],
// [ -5, -10], [ 0, -10], [ 5, -10],
// [ 14, -5], [ 15, 0], [16, 5],
// [ 5, 10], [ 0, 10]
// ];
// trace_bezier(bez, N=3, size=0.5);
module trace_bezier(bez, N=3, size=1) {
trace_polyline(bez, N=N, showpts=true, size=size/2, color="green");
trace_polyline(bezier_polyline(bez, N=N), size=size, color="cyan");
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

253
compat.scad Normal file
View file

@ -0,0 +1,253 @@
//////////////////////////////////////////////////////////////////////
// LibFile: compat.scad
// Backwards Compatability library
// To use, include this line at the top of your file:
// ```
// use <compat.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.
*/
// Section: Functions
// Function: default()
// Description:
// Returns the value given as `v` if it is not `undef`.
// Otherwise, returns the value of `dflt`.
// Arguments:
// v = Value to pass through if not `undef`.
// dflt = Value to return if `v` *is* `undef`.
function default(v,dflt=undef) = v==undef? dflt : v;
// Function: is_def()
// Description: Returns true if given value is not `undef`.
function is_def(v) = (version_num()>20190000)? !is_undef(v) : (v != undef);
// Function: is_str()
// Description: Given a value, returns true if it is a string.
function is_str(v) = (version_num()>20190000)? is_string(v) : (is_def(v) && is_def(len(v)) && (len(str(v,v)) == len(v)*2));
// Function: is_boolean()
// Description: Given a value, returns true if it is a boolean.
function is_boolean(v) = (version_num()>20190000)? is_bool(v) : (!is_str(v) && (str(v) == "true" || str(v) == "false"));
// Function: is_scalar()
// Description: Given a value, returns true if it is a scalar number.
function is_scalar(v) = (version_num()>20190000)? is_num(v) : (!is_boolean(v) && is_def(v+0));
// Function: is_array()
// Description: Given a value, returns true if it is an array/list/vector.
function is_array(v) = (version_num()>20190000)? is_list(v) : (!is_str(v) && is_def(len(v)));
// Function: get_radius()
// Description:
// Given various radii and diameters, returns the most specific radius.
// If a diameter is most specific, returns half its value, giving the radius.
// If no radii or diameters are defined, returns the value of dflt.
// Value specificity order is r1, d1, r, d, then dflt
// Arguments:
// r1 = Most specific radius.
// d1 = Most specific Diameter.
// r = Most general radius.
// d = Most general diameter.
// dflt = Value to return if all other values given are `undef`.
function get_radius(r1=undef, r=undef, d1=undef, d=undef, dflt=undef) = (
is_def(r1)? r1 :
is_def(d1)? d1/2 :
is_def(r)? r :
is_def(d)? d/2 :
dflt
);
// Function: Len()
// Description:
// Given an array, returns number of items in array. Otherwise returns `undef`.
function Len(v) = is_array(v)? len(v) : undef;
// Function: remove_undefs()
// Description: Removes all `undef`s from a list.
function remove_undefs(v) = [for (x = v) if (is_def(x)) x];
// Function: first_defined()
// Description:
// Returns the first item in the list that is not `undef`.
// If all items are `undef`, or list is empty, returns `undef`.
function first_defined(v) = remove_undefs(v)[0];
// Function: any_defined()
// Description:
// Returns true if any item in the given array is not `undef`.
function any_defined(v) = len(remove_undefs(v))>0;
// Function: scalar_vec3()
// Usage:
// scalar_vec3(v, [dflt]);
// Description:
// If `v` is a scalar, and `dflt==undef`, returns `[v, v, v]`.
// If `v` is a scalar, and `dflt!=undef`, returns `[v, dflt, dflt]`.
// If `v` is a vector, returns the first 3 items, with any missing values replaced by `dflt`.
// If `v` is `undef`, returns `undef`.
// Arguments:
// v = Value to return vector from.
// dflt = Default value to set empty vector parts from.
function scalar_vec3(v, dflt=undef) =
!is_def(v)? undef :
is_array(v)? [for (i=[0:2]) default(v[i], default(dflt, 0))] :
is_def(dflt)? [v,dflt,dflt] : [v,v,v];
// Section: Modules
// Module: assert_in_list()
// Usage:
// assert_in_list(argname, val, l, [idx]);
// Description:
// Emulates the newer OpenSCAD `assert()` with an `in_list()` test.
// Arguments:
// argname = The name of the argument value being tested.
// val = The value to test if it exists in the list.
// l = The list to look for `val` in.
// idx = If given, and `l` is a list of lists, look for `val` in the given index of each sublist.
module assert_in_list(argname, val, l, idx=undef) {
succ = search([val], l, num_returns_per_match=1, index_col_num=idx) != [[]];
if (!succ) {
msg = str(
"In argument '", argname, "', ",
(is_string(val)? str("\"", val, "\"") : val),
" must be one of ",
(is_def(idx)? [for (v=l) v[idx]] : l)
);
assertion(succ, msg);
}
}
// Module: assertion()
// Usage:
// assertion(succ, msg);
// Description:
// Backwards compatible assert() semi-replacement. If `succ` is false, then print an error with `msg`.
// Arguments:
// succ = If this is `false`, trigger the assertion.
// msg = The message to emit if `succ` is `false`.
module assertion(succ, msg) {
if (version_num() > 20190000) {
// assert() will echo the variable name, and `succ` looks confusing there. So we store it in FAILED.
FAILED = succ;
assert(FAILED, msg);
} else if (!succ) {
echo_error(msg);
}
}
// Module: echo_error()
// Usage:
// echo_error(msg, [pfx]);
// Description: Emulates printing of an error message. The text will be shaded red.
// Arguments:
// msg = The message to print.
// pfx = The prefix to print before `msg`. Default: `ERROR`
module echo_error(msg, pfx="ERROR") {
echo(str("<p style=\"background-color: #a00\"><b>", pfx, ":</b> ", msg, "</p>"));
}
// Module: echo_warning()
// Usage:
// echo_warning(msg, [pfx]);
// Description: Emulates printing of a warning message. The text will be shaded yellow.
// Arguments:
// msg = The message to print.
// pfx = The prefix to print before `msg`. Default: `WARNING`
module echo_warning(msg, pfx="WARNING") {
echo(str("<p style=\"background-color: #ee0\"><b>", pfx, ":</b> ", msg, "</p>"));
}
// Module: deprecate()
// Usage:
// deprecate(name, [suggest]);
// Description: Show module deprecation warnings.
// Arguments:
// name = The name of the module that is deprecated.
// suggest = If given, the module to recommend using instead.
module deprecate(name, suggest=undef) {
echo_warning(pfx="DEPRECATED",
str(
"`<code>", name, "</code>` is deprecated and should not be used.",
!is_def(suggest)? "" : str(
" You should use `<code>", suggest, "</code>` instead."
)
)
);
}
// Module: deprecate_argument()
// Usage:
// deprecate(name, arg, [suggest]);
// Description: Show module argument deprecation warnings.
// Arguments:
// name = The name of the module the deprecated argument is used in.
// arg = The name of the deprecated argument.
// suggest = If given, the argument to recommend using instead.
module deprecate_argument(name, arg, suggest=undef) {
echo(str(
"<p style=\"background-color: #ee0\">",
"<b>DEPRECATED ARGUMENT:</b> In `<code>", name, "</code>`, ",
"the argument `<code>", arg, "</code>` ",
"is deprecated and should not be used.",
!is_def(suggest)? "" : str(
" You should use `<code>", suggest, "</code>` instead."
),
"</p>"
));
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,5 +1,10 @@
//////////////////////////////////////////////////////////////////////
// Useful Constants
// LibFile: constants.scad
// Useful Constants.
// To use this, add the following line to the top of your file.
// ```
// include <BOSL/constants.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,79 +36,273 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Vectors useful for mirror(), offsetcube(), rotate(), etc.
// Section: General Constants
PRINTER_SLOP = 0.20; // The printer specific amount of slop in mm to print with to make parts fit exactly. You may need to override this value for your printer.
// Section: Directional Vectors
// Vectors useful for `rotate()`, `mirror()`, and `align` arguments for `cuboid()`, `cyl()`, etc.
// Constant: V_LEFT
// Description: Vector pointing left. [-1,0,0]
// Example(3D): Usage with `align`
// cuboid(20, align=V_LEFT);
V_LEFT = [-1, 0, 0];
// Constant: V_RIGHT
// Description: Vector pointing right. [1,0,0]
// Example(3D): Usage with `align`
// cuboid(20, align=V_RIGHT);
V_RIGHT = [ 1, 0, 0];
// Constant: V_FWD
// Description: Vector pointing forward. [0,-1,0]
// Example(3D): Usage with `align`
// cuboid(20, align=V_FWD);
V_FWD = [ 0, -1, 0];
// Constant: V_BACK
// Description: Vector pointing back. [0,1,0]
// Example(3D): Usage with `align`
// cuboid(20, align=V_BACK);
V_BACK = [ 0, 1, 0];
// Constant: V_DOWN
// Description: Vector pointing down. [0,0,-1]
// Example(3D): Usage with `align`
// cuboid(20, align=V_DOWN);
V_DOWN = [ 0, 0, -1];
// Constant: V_UP
// Description: Vector pointing up. [0,0,1]
// Example(3D): Usage with `align`
// cuboid(20, align=V_UP);
V_UP = [ 0, 0, 1];
V_ZERO = [ 0, 0, 0];
// Constant: V_ALLPOS
// Description: Vector pointing right, back, and up. [1,1,1]
// Example(3D): Usage with `align`
// cuboid(20, align=V_ALLPOS);
V_ALLPOS = [ 1, 1, 1]; // Vector pointing X+,Y+,Z+.
// Constant: V_ALLNEG
// Description: Vector pointing left, forwards, and down. [-1,-1,-1]
// Example(3D): Usage with `align`
// cuboid(20, align=V_ALLNEG);
V_ALLNEG = [-1, -1, -1]; // Vector pointing X-,Y-,Z-.
// Constant: V_ZERO
// Description: Zero vector. Centered. [0,0,0]
// Example(3D): Usage with `align`
// cuboid(20, align=V_ZERO);
V_ZERO = [ 0, 0, 0]; // Centered zero vector.
// Orientations for cyl(), etc. Euller angles for rotating a vertical shape into the given orientations.
ORIENT_X = [ 0, 90, 0];
ORIENT_Y = [-90, 0, 0];
ORIENT_Z = [ 0, 0, 0];
ORIENT_XNEG = [ 0,-90, 0];
ORIENT_YNEG = [ 90, 0, 0];
ORIENT_ZNEG = [ 0,180, 0];
// Section: Vector Aliases
// Useful aliases for use with `align`.
V_CENTER = V_ZERO; // Centered, alias to `V_ZERO`.
V_ABOVE = V_UP; // Vector pointing up, alias to `V_UP`.
V_BELOW = V_DOWN; // Vector pointing down, alias to `V_DOWN`.
V_BEFORE = V_FWD; // Vector pointing forward, alias to `V_FWD`.
V_BEHIND = V_BACK; // Vector pointing back, alias to `V_BACK`.
V_TOP = V_UP; // Vector pointing up, alias to `V_UP`.
V_BOTTOM = V_DOWN; // Vector pointing down, alias to `V_DOWN`.
V_FRONT = V_FWD; // Vector pointing forward, alias to `V_FWD`.
V_REAR = V_BACK; // Vector pointing back, alias to `V_BACK`.
// Constants for defining edges for chamfer(), etc.
EDGE_TOP_BK = [[1,0,0,0], [0,0,0,0], [0,0,0,0]];
EDGE_TOP_FR = [[0,1,0,0], [0,0,0,0], [0,0,0,0]];
EDGE_BOT_FR = [[0,0,1,0], [0,0,0,0], [0,0,0,0]];
EDGE_BOT_BK = [[0,0,0,1], [0,0,0,0], [0,0,0,0]];
EDGE_TOP_RT = [[0,0,0,0], [1,0,0,0], [0,0,0,0]];
EDGE_TOP_LF = [[0,0,0,0], [0,1,0,0], [0,0,0,0]];
EDGE_BOT_LF = [[0,0,0,0], [0,0,1,0], [0,0,0,0]];
EDGE_BOT_RT = [[0,0,0,0], [0,0,0,1], [0,0,0,0]];
EDGE_BK_RT = [[0,0,0,0], [0,0,0,0], [1,0,0,0]];
EDGE_BK_LF = [[0,0,0,0], [0,0,0,0], [0,1,0,0]];
EDGE_FR_LF = [[0,0,0,0], [0,0,0,0], [0,0,1,0]];
EDGE_FR_RT = [[0,0,0,0], [0,0,0,0], [0,0,0,1]];
EDGES_X_TOP = [[1,1,0,0], [0,0,0,0], [0,0,0,0]];
EDGES_X_BOT = [[0,0,1,1], [0,0,0,0], [0,0,0,0]];
EDGES_X_FR = [[0,1,1,0], [0,0,0,0], [0,0,0,0]];
EDGES_X_BK = [[1,0,0,1], [0,0,0,0], [0,0,0,0]];
EDGES_X_ALL = [[1,1,1,1], [0,0,0,0], [0,0,0,0]];
EDGES_Y_TOP = [[0,0,0,0], [1,1,0,0], [0,0,0,0]];
EDGES_Y_BOT = [[0,0,0,0], [0,0,1,1], [0,0,0,0]];
EDGES_Y_LF = [[0,0,0,0], [0,1,1,0], [0,0,0,0]];
EDGES_Y_RT = [[0,0,0,0], [1,0,0,1], [0,0,0,0]];
EDGES_Y_ALL = [[0,0,0,0], [1,1,1,1], [0,0,0,0]];
EDGES_Z_BK = [[0,0,0,0], [0,0,0,0], [1,1,0,0]];
EDGES_Z_FR = [[0,0,0,0], [0,0,0,0], [0,0,1,1]];
EDGES_Z_LF = [[0,0,0,0], [0,0,0,0], [0,1,1,0]];
EDGES_Z_RT = [[0,0,0,0], [0,0,0,0], [1,0,0,1]];
EDGES_Z_ALL = [[0,0,0,0], [0,0,0,0], [1,1,1,1]];
EDGES_LEFT = [[0,0,0,0], [0,1,1,0], [0,1,1,0]];
EDGES_RIGHT = [[0,0,0,0], [1,0,0,1], [1,0,0,1]];
EDGES_FRONT = [[0,1,1,0], [0,0,0,0], [0,0,1,1]];
EDGES_BACK = [[1,0,0,1], [0,0,0,0], [1,1,0,0]];
EDGES_BOTTOM = [[0,0,1,1], [0,0,1,1], [0,0,0,0]];
EDGES_TOP = [[1,1,0,0], [1,1,0,0], [0,0,0,0]];
EDGES_NONE = [[0,0,0,0], [0,0,0,0], [0,0,0,0]];
EDGES_ALL = [[1,1,1,1], [1,1,1,1], [1,1,1,1]];
// Section: Pre-Orientation Alignments
// Constants for pre-orientation alignments.
EDGE_OFFSETS = [
// Constant: ALIGN_POS
// Description: Align the axis-positive end to the origin.
// Example(3D): orient=ORIENT_X
// cyl(d1=10, d2=5, h=20, orient=ORIENT_X, align=ALIGN_POS);
// Example(3D): orient=ORIENT_Y
// cyl(d1=10, d2=5, h=20, orient=ORIENT_Y, align=ALIGN_POS);
// Example(3D): orient=ORIENT_Z
// cyl(d1=10, d2=5, h=20, orient=ORIENT_Z, align=ALIGN_POS);
// Example(3D): orient=ORIENT_XNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_XNEG, align=ALIGN_POS);
// Example(3D): orient=ORIENT_YNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_YNEG, align=ALIGN_POS);
// Example(3D): orient=ORIENT_ZNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_ZNEG, align=ALIGN_POS);
ALIGN_POS = 1;
ALIGN_CENTER = 0; // Align centered.
// Constant: ALIGN_NEG
// Description: Align the axis-negative end to the origin.
// Example(3D): orient=ORIENT_X
// cyl(d1=10, d2=5, h=20, orient=ORIENT_X, align=ALIGN_NEG);
// Example(3D): orient=ORIENT_Y
// cyl(d1=10, d2=5, h=20, orient=ORIENT_Y, align=ALIGN_NEG);
// Example(3D): orient=ORIENT_Z
// cyl(d1=10, d2=5, h=20, orient=ORIENT_Z, align=ALIGN_NEG);
// Example(3D): orient=ORIENT_XNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_XNEG, align=ALIGN_NEG);
// Example(3D): orient=ORIENT_YNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_YNEG, align=ALIGN_NEG);
// Example(3D): orient=ORIENT_ZNEG
// cyl(d1=10, d2=5, h=20, orient=ORIENT_ZNEG, align=ALIGN_NEG);
ALIGN_NEG = -1;
// CommonCode:
// orientations = [
// ORIENT_X, ORIENT_Y, ORIENT_Z,
// ORIENT_XNEG, ORIENT_YNEG, ORIENT_ZNEG,
// ORIENT_X_90, ORIENT_Y_90, ORIENT_Z_90,
// ORIENT_XNEG_90, ORIENT_YNEG_90, ORIENT_ZNEG_90,
// ORIENT_X_180, ORIENT_Y_180, ORIENT_Z_180,
// ORIENT_XNEG_180, ORIENT_YNEG_180, ORIENT_ZNEG_180,
// ORIENT_X_270, ORIENT_Y_270, ORIENT_Z_270,
// ORIENT_XNEG_270, ORIENT_YNEG_270, ORIENT_ZNEG_270
// ];
// axiscolors = ["red", "forestgreen", "dodgerblue"];
// module text3d(text, h=0.01, size=3) {
// linear_extrude(height=h, convexity=10) {
// text(text=text, size=size, valign="center", halign="center");
// }
// }
// module orient_cube(ang) {
// color("lightgray") cube(20, center=true);
// color(axiscolors.x) up ((20-1)/2+0.01) back ((20-1)/2+0.01) cube([18,1,1], center=true);
// color(axiscolors.y) up ((20-1)/2+0.01) right((20-1)/2+0.01) cube([1,18,1], center=true);
// color(axiscolors.z) back((20-1)/2+0.01) right((20-1)/2+0.01) cube([1,1,18], center=true);
// for (axis=[0:2], neg=[0:1]) {
// idx = axis + 3*neg + 6*ang/90;
// rotate(orientations[idx]) {
// up(10) {
// back(4) color("black") text3d(text=str(ang), size=4);
// fwd(4) color(axiscolors[axis]) text3d(text=str(["X","Y","Z"][axis], ["+","NEG"][neg]), size=4);
// }
// }
// }
// }
// Section: Standard Orientations
// Orientations for `cyl()`, `prismoid()`, etc. They take the form of standard [X,Y,Z]
// rotation angles for rotating a vertical shape into the given orientations.
// Figure(Spin): Standard Orientations
// orient_cube(0);
ORIENT_X = [ 90, 0, 90]; // Orient along the X axis.
ORIENT_Y = [ 90, 0, 180]; // Orient along the Y axis.
ORIENT_Z = [ 0, 0, 0]; // Orient along the Z axis.
ORIENT_XNEG = [ 90, 0, -90]; // Orient reversed along the X axis.
ORIENT_YNEG = [ 90, 0, 0]; // Orient reversed along the Y axis.
ORIENT_ZNEG = [ 0, 180, 0]; // Orient reversed along the Z axis.
// Section: Orientations Rotated 90º
// Orientations for `cyl()`, `prismoid()`, etc. They take the form of standard [X,Y,Z]
// rotation angles for rotating a vertical shape into the given orientations.
// Figure(Spin): Orientations Rotated 90º
// orient_cube(90);
ORIENT_X_90 = [ 90, -90, 90]; // Orient along the X axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Y_90 = [ 90, -90, 180]; // Orient along the Y axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Z_90 = [ 0, 0, 90]; // Orient along the Z axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_XNEG_90 = [ 0, -90, 0]; // Orient reversed along the X axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_YNEG_90 = [ 90, -90, 0]; // Orient reversed along the Y axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_ZNEG_90 = [ 0, 180, -90]; // Orient reversed along the Z axis, then rotate 90 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
// Section: Orientations Rotated 180º
// Orientations for `cyl()`, `prismoid()`, etc. They take the form of standard [X,Y,Z]
// rotation angles for rotating a vertical shape into the given orientations.
// Figure(Spin): Orientations Rotated 180º
// orient_cube(180);
ORIENT_X_180 = [-90, 0, -90]; // Orient along the X axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Y_180 = [-90, 0, 0]; // Orient along the Y axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Z_180 = [ 0, 0, 180]; // Orient along the Z axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_XNEG_180 = [-90, 0, 90]; // Orient reversed along the X axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_YNEG_180 = [-90, 0, 180]; // Orient reversed along the Y axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_ZNEG_180 = [ 0, 180, 180]; // Orient reversed along the Z axis, then rotate 180 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
// Section: Orientations Rotated 270º
// Orientations for `cyl()`, `prismoid()`, etc. They take the form of standard [X,Y,Z]
// rotation angles for rotating a vertical shape into the given orientations.
// Figure(Spin): Orientations Rotated 270º
// orient_cube(270);
ORIENT_X_270 = [ 90, 90, 90]; // Orient along the X axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Y_270 = [ 90, 90, 180]; // Orient along the Y axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_Z_270 = [ 0, 0, -90]; // Orient along the Z axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_XNEG_270 = [ 90, 90, -90]; // Orient reversed along the X axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_YNEG_270 = [ 90, 90, 0]; // Orient reversed along the Y axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
ORIENT_ZNEG_270 = [ 0, 180, 90]; // Orient reversed along the Z axis, then rotate 270 degrees counter-clockwise on that axis, as seen when facing the origin from that axis orientation.
// Section: Individual Edges
// Constants for specifying edges for `cuboid()`, etc.
EDGE_TOP_BK = [[1,0,0,0], [0,0,0,0], [0,0,0,0]]; // Top Back edge.
EDGE_TOP_FR = [[0,1,0,0], [0,0,0,0], [0,0,0,0]]; // Top Front edge.
EDGE_BOT_FR = [[0,0,1,0], [0,0,0,0], [0,0,0,0]]; // Bottom Front Edge.
EDGE_BOT_BK = [[0,0,0,1], [0,0,0,0], [0,0,0,0]]; // Bottom Back Edge.
EDGE_TOP_RT = [[0,0,0,0], [1,0,0,0], [0,0,0,0]]; // Top Right edge.
EDGE_TOP_LF = [[0,0,0,0], [0,1,0,0], [0,0,0,0]]; // Top Left edge.
EDGE_BOT_LF = [[0,0,0,0], [0,0,1,0], [0,0,0,0]]; // Bottom Left edge.
EDGE_BOT_RT = [[0,0,0,0], [0,0,0,1], [0,0,0,0]]; // Bottom Right edge.
EDGE_BK_RT = [[0,0,0,0], [0,0,0,0], [1,0,0,0]]; // Back Right edge.
EDGE_BK_LF = [[0,0,0,0], [0,0,0,0], [0,1,0,0]]; // Back Left edge.
EDGE_FR_LF = [[0,0,0,0], [0,0,0,0], [0,0,1,0]]; // Front Left edge.
EDGE_FR_RT = [[0,0,0,0], [0,0,0,0], [0,0,0,1]]; // Front Right edge.
// Section: Sets of Edges
// Constants for specifying edges for `cuboid()`, etc.
EDGES_X_TOP = [[1,1,0,0], [0,0,0,0], [0,0,0,0]]; // Both X-aligned Top edges.
EDGES_X_BOT = [[0,0,1,1], [0,0,0,0], [0,0,0,0]]; // Both X-aligned Bottom edges.
EDGES_X_FR = [[0,1,1,0], [0,0,0,0], [0,0,0,0]]; // Both X-aligned Front edges.
EDGES_X_BK = [[1,0,0,1], [0,0,0,0], [0,0,0,0]]; // Both X-aligned Back edges.
EDGES_X_ALL = [[1,1,1,1], [0,0,0,0], [0,0,0,0]]; // All four X-aligned edges.
EDGES_Y_TOP = [[0,0,0,0], [1,1,0,0], [0,0,0,0]]; // Both Y-aligned Top edges.
EDGES_Y_BOT = [[0,0,0,0], [0,0,1,1], [0,0,0,0]]; // Both Y-aligned Bottom edges.
EDGES_Y_LF = [[0,0,0,0], [0,1,1,0], [0,0,0,0]]; // Both Y-aligned Left edges.
EDGES_Y_RT = [[0,0,0,0], [1,0,0,1], [0,0,0,0]]; // Both Y-aligned Right edges.
EDGES_Y_ALL = [[0,0,0,0], [1,1,1,1], [0,0,0,0]]; // All four Y-aligned edges.
EDGES_Z_BK = [[0,0,0,0], [0,0,0,0], [1,1,0,0]]; // Both Z-aligned Back edges.
EDGES_Z_FR = [[0,0,0,0], [0,0,0,0], [0,0,1,1]]; // Both Z-aligned Front edges.
EDGES_Z_LF = [[0,0,0,0], [0,0,0,0], [0,1,1,0]]; // Both Z-aligned Left edges.
EDGES_Z_RT = [[0,0,0,0], [0,0,0,0], [1,0,0,1]]; // Both Z-aligned Right edges.
EDGES_Z_ALL = [[0,0,0,0], [0,0,0,0], [1,1,1,1]]; // All four Z-aligned edges.
EDGES_LEFT = [[0,0,0,0], [0,1,1,0], [0,1,1,0]]; // All four Left edges.
EDGES_RIGHT = [[0,0,0,0], [1,0,0,1], [1,0,0,1]]; // All four Right edges.
EDGES_FRONT = [[0,1,1,0], [0,0,0,0], [0,0,1,1]]; // All four Front edges.
EDGES_BACK = [[1,0,0,1], [0,0,0,0], [1,1,0,0]]; // All four Back edges.
EDGES_BOTTOM = [[0,0,1,1], [0,0,1,1], [0,0,0,0]]; // All four Bottom edges.
EDGES_TOP = [[1,1,0,0], [1,1,0,0], [0,0,0,0]]; // All four Top edges.
EDGES_NONE = [[0,0,0,0], [0,0,0,0], [0,0,0,0]]; // No edges.
EDGES_ALL = [[1,1,1,1], [1,1,1,1], [1,1,1,1]]; // All edges.
// Section: Edge Helpers
EDGE_OFFSETS = [ // Array of XYZ offsets to the center of each edge.
[[0, 1, 1], [ 0,-1, 1], [ 0,-1,-1], [0, 1,-1]],
[[1, 0, 1], [-1, 0, 1], [-1, 0,-1], [1, 0,-1]],
[[1, 1, 0], [-1, 1, 0], [-1,-1, 0], [1,-1, 0]]
];
// Function: corner_edge_count()
// Description: Counts how many given edges intersect at a specific corner.
// Arguments:
// edges = Standard edges array.
// v = Vector pointing to the corner to count edge intersections at.
function corner_edge_count(edges, v) =
(v[2]<=0)? (
(v[1]<=0)? (
@ -136,4 +335,5 @@ function corner_edge_count(edges, v) =
);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Helpers to make debugging OpenScad code easier.
// LibFile: debug.scad
// Helpers to make debugging OpenScad code easier.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/debug.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -36,76 +42,21 @@ include <paths.scad>
include <beziers.scad>
// Renders lines between each point of a polyline path.
// Can also optionally show the individual vertex points.
// pline = the array of points in the polyline.
// showpts = If true, draw vertices and control points.
// N = Mark the first and every Nth vertex after in a different color and shape.
// size = diameter of the lines drawn.
// color = Color to draw the lines (but not vertices) in.
// Example:
// bez = [
// [-10, 0, 0], [-15, -5, 9], [0, -3, 5], [5, -10, 0],
// [15, 0, -5], [5, 12, -8], [0, 10, -5]
// ];
// trace_polyline(bez, N=3, showpts=true, size=0.5, color="lightgreen");
module trace_polyline(pline, N=1, showpts=false, size=1, color="yellow") {
if (showpts) {
for (i = [0:len(pline)-1]) {
translate(pline[i]) {
if (i%N == 0) {
color("blue") sphere(d=size*2.5, $fn=8);
} else {
color("red") {
cylinder(d=size/2, h=size*3, center=true, $fn=8);
xrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
yrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
}
}
}
}
}
for (i = [0:len(pline)-2]) {
if (N!=3 || (i%N) != 1) {
color(color) extrude_from_to(pline[i], pline[i+1]) circle(d=size/2);
}
}
}
// Section: Debugging Polyhedrons
// Renders lines between each point of a polyline path.
// Can also optionally show the individual vertex points.
// bez = the array of points in the bezier.
// N = Mark the first and every Nth vertex after in a different color and shape.
// size = diameter of the lines drawn.
// Example:
// bez = [
// [-10, 0], [-15, -5],
// [ -5, -10], [ 0, -10], [ 5, -10],
// [ 14, -5], [ 15, 0], [16, 5],
// [ 5, 10], [ 0, 10]
// ];
// trace_bezier(bez, N=3, size=0.5);
module trace_bezier(bez, N=3, size=1) {
trace_polyline(bez, N=N, showpts=true, size=size/2, color="green");
trace_polyline(bezier_polyline(bez, N=N), size=size);
}
// Draws all the vertices in an array, at their 3D position, numbered by their
// position in the vertex array. Also draws any children of this module with
// transparency.
// Module: debug_vertices()
// Description:
// Draws all the vertices in an array, at their 3D position, numbered by their
// position in the vertex array. Also draws any children of this module with
// transparency.
// Arguments:
// vertices = Array of point vertices.
// size = The size of the text used to label the vertices.
// disabled = If true, don't draw numbers, and draw children without transparency. Default = false.
// Example:
// verts = [
// [-10, 0, -10], [10, 0, -10],
// [0, -10, 10], [0, 10, 10]
// ];
// faces = [
// [0,2,1], [1,2,3], [0,3,2], [1,3,0]
// ];
// verts = [for (z=[-10,10], y=[-10,10], x=[-10,10]) [x,y,z]];
// faces = [[0,1,2], [1,3,2], [0,4,5], [0,5,1], [1,5,7], [1,7,3], [3,7,6], [3,6,2], [2,6,4], [2,4,0], [4,6,7], [4,7,5]];
// debug_vertices(vertices=verts, size=2) {
// polyhedron(points=verts, faces=faces);
// }
@ -128,7 +79,7 @@ module debug_vertices(vertices, size=1, disabled=false) {
}
if ($children > 0) {
if (!disabled) {
color([0.5, 0.5, 0, 0.25]) children();
color([0.2, 1.0, 0, 0.5]) children();
} else {
children();
}
@ -137,22 +88,20 @@ module debug_vertices(vertices, size=1, disabled=false) {
// Draws all the vertices at their 3D position, numbered in blue by their
// position in the vertex array. Each face will have their face number drawn
// in red, aligned with the center of face. All children of this module are drawn
// with transparency.
// Module: debug_faces()
// Description:
// Draws all the vertices at their 3D position, numbered in blue by their
// position in the vertex array. Each face will have their face number drawn
// in red, aligned with the center of face. All children of this module are drawn
// with transparency.
// Arguments:
// vertices = Array of point vertices.
// faces = Array of faces by vertex numbers.
// size = The size of the text used to label the faces and vertices.
// disabled = If true, don't draw numbers, and draw children without transparency. Default = false.
// Example:
// verts = [
// [-10, 0, -10], [10, 0, -10],
// [0, -10, 10], [0, 10, 10]
// ];
// faces = [
// [0,2,1], [1,2,3], [0,3,2], [1,3,0]
// ];
// verts = [for (z=[-10,10], y=[-10,10], x=[-10,10]) [x,y,z]];
// faces = [[0,1,2], [1,3,2], [0,4,5], [0,5,1], [1,5,7], [1,7,3], [3,7,6], [3,6,2], [2,6,4], [2,4,0], [4,6,7], [4,7,5]];
// debug_faces(vertices=verts, faces=faces, size=2) {
// polyhedron(points=verts, faces=faces);
// }
@ -201,20 +150,23 @@ module debug_faces(vertices, faces, size=1, disabled=false) {
// A drop-in module to replace `polyhedron()` and help debug vertices and faces.
// Draws all the vertices at their 3D position, numbered in blue by their
// position in the vertex array. Each face will have their face number drawn
// in red, aligned with the center of face. All given faces are drawn with
// transparency. All children of this module are drawn with transparency.
// Works best with Thrown-Together preview mode, to see reversed faces.
// Module: debug_polyhedron()
// Description:
// A drop-in module to replace `polyhedron()` and help debug vertices and faces.
// Draws all the vertices at their 3D position, numbered in blue by their
// position in the vertex array. Each face will have their face number drawn
// in red, aligned with the center of face. All given faces are drawn with
// transparency. All children of this module are drawn with transparency.
// Works best with Thrown-Together preview mode, to see reversed faces.
// Arguments:
// vertices = Array of point vertices.
// faces = Array of faces by vertex numbers.
// txtsize = The size of the text used to label the faces and vertices.
// disabled = If true, act exactly like `polyhedron()`. Default = false.
// Example:
// pts = [[-5,0,-5], [5,0,-5], [0,-5,5], [0,5,5]];
// fcs = [[0,2,1], [1,2,3], [1,3,0], [0,2,3]]; // Last face reversed
// debug_polyhedron(points=pts, faces=fcs, txtsize=1);
// verts = [for (z=[-10,10], a=[0:120:359.9]) [10*cos(a),10*sin(a),z]];
// faces = [[0,1,2], [5,4,3], [0,3,4], [0,4,1], [1,4,5], [1,5,2], [2,5,3], [2,3,0]];
// debug_polyhedron(points=verts, faces=faces, txtsize=1);
module debug_polyhedron(points, faces, convexity=10, txtsize=1, disabled=false) {
debug_faces(vertices=points, faces=faces, size=txtsize, disabled=disabled) {
polyhedron(points=points, faces=faces, convexity=convexity);

View file

@ -0,0 +1,11 @@
{
"parameterSets": {
"design default values": {
"axiscolors": "[\"red\", \"forestgreen\", \"dodgerblue\"]",
"axisdiam": "0.5",
"axislbllen": "15",
"axislen": "9"
}
},
"fileFormatVersion": "1"
}

View file

@ -0,0 +1,83 @@
use <BOSL/transforms.scad>
use <BOSL/math.scad>
include <BOSL/constants.scad>
// Shows all the orientations on cubes in their correct rotations.
orientations = [
ORIENT_X, ORIENT_Y, ORIENT_Z,
ORIENT_XNEG, ORIENT_YNEG, ORIENT_ZNEG,
ORIENT_X_90, ORIENT_Y_90, ORIENT_Z_90,
ORIENT_XNEG_90, ORIENT_YNEG_90, ORIENT_ZNEG_90,
ORIENT_X_180, ORIENT_Y_180, ORIENT_Z_180,
ORIENT_XNEG_180, ORIENT_YNEG_180, ORIENT_ZNEG_180,
ORIENT_X_270, ORIENT_Y_270, ORIENT_Z_270,
ORIENT_XNEG_270, ORIENT_YNEG_270, ORIENT_ZNEG_270
];
axisdiam = 0.5;
axislen = 12;
axislbllen = 15;
axiscolors = ["red", "forestgreen", "dodgerblue"];
module text3d(text, h=0.01, size=3) {
linear_extrude(height=h, convexity=10) {
text(text=text, size=size, valign="center", halign="center");
}
}
module dottedline(l, d) for(y = [0:d*3:l]) up(y) sphere(d=d);
module orient_cubes() {
color(axiscolors[0]) {
yrot( 90) cylinder(h=axislen, d=axisdiam, center=false);
right(axislbllen) rot([90,0,0]) text3d(text="X+");
yrot(-90) dottedline(l=axislen, d=axisdiam);
left(axislbllen) rot([90,0,180]) text3d(text="X-");
}
color(axiscolors[1]) {
xrot(-90) cylinder(h=axislen, d=axisdiam, center=false);
back(axislbllen) rot([90,0,90]) text3d(text="Y+");
xrot( 90) dottedline(l=axislen, d=axisdiam);
fwd(axislbllen) rot([90,0,-90]) text3d(text="Y-");
}
color(axiscolors[2]) {
cylinder(h=axislen, d=axisdiam, center=false);
up(axislbllen) rot([0,-90,90+$vpr[2]]) text3d(text="Z+");
xrot(180) dottedline(l=axislen, d=axisdiam);
down(axislbllen) rot([0,90,-90+$vpr[2]]) text3d(text="Z-");
}
for (ang = [0:90:270]) {
translate(cylindrical_to_xyz(40, ang+90, 0)) {
color("lightgray") cube(20, center=true);
}
}
for (axis=[0:2], neg=[0:1], ang = [0:90:270]) {
idx = axis + 3*neg + 6*ang/90;
translate(cylindrical_to_xyz(40, ang+90, 0)) {
rotate(orientations[idx]) {
up(10) {
ydistribute(8) {
color("black") text3d(text=str(ang, "º"), size=5);
color(axiscolors[axis]) text3d(text=str(["X","Y","Z"][axis], ["+","-"][neg]), size=5);
}
}
}
}
}
}
//rotate(a=180, v=[1,1,0])
orient_cubes();
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,70 +1,160 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// Public Domain Parametric Involute Spur Gear (and involute helical gear and involute rack)
// version 1.1
// by Leemon Baird, 2011, Leemon@Leemon.com
// http://www.thingiverse.com/thing:5505
// Tweaked, and improved by Revar Desmera, 2017-2019, revarbat@gmail.com
//
// This file is public domain. Use it for any purpose, including commercial
// applications. Attribution would be nice, but is not required. There is
// no warranty of any kind, including its correctness, usefulness, or safety.
//
// This is parameterized involute spur (or helical) gear. It is much simpler and less powerful than
// others on Thingiverse. But it is public domain. I implemented it from scratch from the
// descriptions and equations on Wikipedia and the web, using Mathematica for calculations and testing,
// and I now release it into the public domain.
//
// http://en.wikipedia.org/wiki/Involute_gear
// http://en.wikipedia.org/wiki/Gear
// http://en.wikipedia.org/wiki/List_of_gear_nomenclature
// http://gtrebaol.free.fr/doc/catia/spur_gear.html
// http://www.cs.cmu.edu/~rapidproto/mechanisms/chpt7.html
//
// The module gear() gives an involute spur gear, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// The module gear() gives a gear in the XY plane, centered on the origin, with one tooth centered on
// the positive Y axis. The various functions below it take the same parameters, and return various
// measurements for the gear. The most important is pitch_radius, which tells how far apart to space
// gears that are meshing, and adendum_radius, which gives the size of the region filled by the gear.
// A gear has a "pitch circle", which is an invisible circle that cuts through the middle of each
// tooth (though not the exact center). In order for two gears to mesh, their pitch circles should
// just touch. So the distance between their centers should be pitch_radius() for one, plus pitch_radius()
// for the other, which gives the radii of their pitch circles.
//
// In order for two gears to mesh, they must have the same mm_per_tooth and pressure_angle parameters.
// mm_per_tooth gives the number of millimeters of arc around the pitch circle covered by one tooth and one
// space between teeth. The pitch angle controls how flat or bulged the sides of the teeth are. Common
// values include 14.5 degrees and 20 degrees, and occasionally 25. Though I've seen 28 recommended for
// plastic gears. Larger numbers bulge out more, giving stronger teeth, so 28 degrees is the default here.
//
// The ratio of number_of_teeth for two meshing gears gives how many times one will make a full
// revolution when the the other makes one full revolution. If the two numbers are coprime (i.e.
// are not both divisible by the same number greater than 1), then every tooth on one gear
// will meet every tooth on the other, for more even wear. So coprime numbers of teeth are good.
//
// The module rack() gives a rack, which is a bar with teeth. A rack can mesh with any
// gear that has the same mm_per_tooth and pressure_angle.
//
// Some terminology:
// The outline of a gear is a smooth circle (the "pitch circle") which has mountains and valleys
// added so it is toothed. So there is an inner circle (the "root circle") that touches the
// base of all the teeth, an outer circle that touches the tips of all the teeth,
// and the invisible pitch circle in between them. There is also a "base circle", which can be smaller than
// all three of the others, which controls the shape of the teeth. The side of each tooth lies on the path
// that the end of a string would follow if it were wrapped tightly around the base circle, then slowly unwound.
// That shape is an "involute", which gives this type of gear its name.
//
// LibFile: involute_gears.scad
// Involute Spur Gears and Racks
//
// by Leemon Baird, 2011, Leemon@Leemon.com
// http://www.thingiverse.com/thing:5505
//
// Additional fixes and improvements by Revar Desmera, 2017-2019, revarbat@gmail.com
//
// This file is public domain. Use it for any purpose, including commercial
// applications. Attribution would be nice, but is not required. There is
// no warranty of any kind, including its correctness, usefulness, or safety.
//
// This is parameterized involute spur (or helical) gear. It is much simpler
// and less powerful than others on Thingiverse. But it is public domain. I
// implemented it from scratch from the descriptions and equations on Wikipedia
// and the web, using Mathematica for calculations and testing, and I now
// release it into the public domain.
//
// To use, add the following line to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/involute_gears.scad>
// ```
//////////////////////////////////////////////////////////////////////////////////////////////
//gear_tooth_profile(mm_per_tooth=5, number_of_teeth=20, pressure_angle=20);
use <transforms.scad>
include <constants.scad>
// Section: Terminology
// The outline of a gear is a smooth circle (the "pitch circle") which has
// mountains and valleys added so it is toothed. There is an inner
// circle (the "root circle") that touches the base of all the teeth, an
// outer circle that touches the tips of all the teeth, and the invisible
// pitch circle in between them. There is also a "base circle", which can
// be smaller than all three of the others, which controls the shape of
// the teeth. The side of each tooth lies on the path that the end of a
// string would follow if it were wrapped tightly around the base circle,
// then slowly unwound. That shape is an "involute", which gives this
// type of gear its name.
// Section: Functions
// These functions let the user find the derived dimensions of the gear.
// A gear fits within a circle of radius outer_radius, and two gears should have
// their centers separated by the sum of their pitch_radius.
// Function: circular_pitch()
// Description: Get tooth density expressed as "circular pitch".
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
function circular_pitch(mm_per_tooth=5) = mm_per_tooth;
// Function: diametral_pitch()
// Description: Get tooth density expressed as "diametral pitch".
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
function diametral_pitch(mm_per_tooth=5) = PI / mm_per_tooth;
// Function: module_value()
// Description: Get tooth density expressed as "module" or "modulus" in millimeters
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
function module_value(mm_per_tooth=5) = mm_per_tooth / PI;
// Function: adendum()
// Description: The height of the gear tooth above the pitch radius.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
function adendum(mm_per_tooth=5) = module_value(mm_per_tooth);
// Function: dedendum()
// Description: The depth of the gear tooth valley, below the pitch radius.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
// clearance = If given, sets the clearance between meshing teeth.
function dedendum(mm_per_tooth=5, clearance=undef) =
(clearance==undef)? (1.25 * module_value(mm_per_tooth)) : (module_value(mm_per_tooth) + clearance);
// Function: pitch_radius()
// Description: Calculates the pitch radius for the gear.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
// number of teeth = The number of teeth on the gear.
function pitch_radius(mm_per_tooth=5, number_of_teeth=11) =
mm_per_tooth * number_of_teeth / PI / 2;
// Function: outer_radius()
// Description:
// Calculates the outer radius for the gear. The gear fits entirely within a cylinder of this radius.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
// number of teeth = The number of teeth on the gear.
// clearance = If given, sets the clearance between meshing teeth.
// interior = If true, calculate for an interior gear.
function outer_radius(mm_per_tooth=5, number_of_teeth=11, clearance=undef, interior=false) =
pitch_radius(mm_per_tooth, number_of_teeth) +
(interior? dedendum(mm_per_tooth, clearance) : adendum(mm_per_tooth));
// Function: root_radius()
// Description:
// Calculates the root radius for the gear, at the base of the dedendum.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
// number of teeth = The number of teeth on the gear.
// clearance = If given, sets the clearance between meshing teeth.
// interior = If true, calculate for an interior gear.
function root_radius(mm_per_tooth=5, number_of_teeth=11, clearance=undef, interior=false)
= pitch_radius(mm_per_tooth, number_of_teeth) -
(interior? adendum(mm_per_tooth) : dedendum(mm_per_tooth, clearance));
// Function: base_radius()
// Description: Get the base circle for involute teeth.
// Arguments:
// mm_per_tooth = Distance between teeth around the pitch circle, in mm.
// number_of_teeth = The number of teeth on the gear.
// pressure_angle = Pressure angle in degrees. Controls how straight or bulged the tooth sides are.
function base_radius(mm_per_tooth=5, number_of_teeth=11, pressure_angle=28)
= pitch_radius(mm_per_tooth, number_of_teeth) * cos(pressure_angle);
// Section: Modules
// Module: gear_tooth_profile()
// Description:
// Creates the 2D profile for an individual gear tooth.
// Arguments:
// mm_per_tooth = This is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
// number_of_teeth = Total number of teeth along the rack
// pressure_angle = Controls how straight or bulged the tooth sides are. In degrees.
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// bevelang = Angle of beveled gear face.
// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
// interior = If true, create a mask for difference()ing from something else.
// Example(2D):
// gear_tooth_profile(mm_per_tooth=5, number_of_teeth=20, pressure_angle=20);
module gear_tooth_profile(
mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 11, //total number of teeth around the entire perimeter
pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees.
backlash = 0.0, //gap between two meshing teeth, in the direction along the circumference of the pitch circle
bevelang = 0.0, //Gear face angle for bevelled gears.
clearance = undef //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
clearance = undef, //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
interior = false
) {
function polar(r,theta) = r*[sin(theta), cos(theta)]; //convert polar to cartesian coordinates
function iang(r1,r2) = sqrt((r2/r1)*(r2/r1) - 1)/PI*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
@ -72,8 +162,8 @@ module gear_tooth_profile(
function q6(b,s,t,d) = polar(d,s*(iang(b,d)+t)); //point at radius d on the involute curve
p = pitch_radius(mm_per_tooth, number_of_teeth);
c = outer_radius(mm_per_tooth, number_of_teeth);
r = root_radius(mm_per_tooth, number_of_teeth, clearance);
c = outer_radius(mm_per_tooth, number_of_teeth, clearance, interior);
r = root_radius(mm_per_tooth, number_of_teeth, clearance, interior);
b = base_radius(mm_per_tooth, number_of_teeth, pressure_angle);
t = mm_per_tooth/2-backlash/2; //tooth thickness at pitch circle
k = -iang(b, p) - t/2/p/PI*180; //angle to where involute meets base circle on each side of tooth
@ -94,19 +184,27 @@ module gear_tooth_profile(
}
// Creates a 2D involute spur gear, with reasonable defaults for all the parameters.
// Normally, you should just specify the first 2 parameters, and let the rest be default values.
// Meshing gears must match in mm_per_tooth, pressure_angle, and twist,
// and be separated by the sum of their pitch radii, which can be found with pitch_radius().
// Module: gear2d()
// Description:
// Creates a 2D involute spur gear, with reasonable defaults for all the parameters.
// Normally, you should just specify the first 2 parameters, and let the rest be default values.
// Meshing gears must match in mm_per_tooth, pressure_angle, and twist,
// and be separated by the sum of their pitch radii, which can be found with pitch_radius().
// Arguments:
// mm_per_tooth = This is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
// number_of_teeth = Total number of teeth along the rack
// teeth_to_hide = Number of teeth to delete to make this only a fraction of a circle
// pressure_angle = Controls how straight or bulged the tooth sides are. In degrees.
// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// Example:
// bevelang = Angle of beveled gear face.
// interior = If true, create a mask for difference()ing from something else.
// Example(2D): Typical Gear Shape
// gear2d(mm_per_tooth=5, number_of_teeth=20);
// linear_extrude(height=5*20/PI/2/2, scale=0.5) gear2d(mm_per_tooth=5, number_of_teeth=20);
// Example(2D): Lower Pressure Angle
// gear2d(mm_per_tooth=5, number_of_teeth=20, pressure_angle=20);
// Example(2D): Partial Gear
// gear2d(mm_per_tooth=5, number_of_teeth=20, teeth_to_hide=15, pressure_angle=20);
module gear2d(
mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 11, //total number of teeth around the entire perimeter
@ -114,9 +212,10 @@ module gear2d(
pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees.
clearance = undef, //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
backlash = 0.0, //gap between two meshing teeth, in the direction along the circumference of the pitch circle
bevelang = 0.0
bevelang = 0.0,
interior = false
) {
r = root_radius(mm_per_tooth, number_of_teeth, clearance);
r = root_radius(mm_per_tooth, number_of_teeth, clearance, interior);
ang = 360/number_of_teeth/2;
union() {
for (i = [0:number_of_teeth-teeth_to_hide-1] ) {
@ -128,7 +227,8 @@ module gear2d(
pressure_angle = pressure_angle,
clearance = clearance,
backlash = backlash,
bevelang = bevelang
bevelang = bevelang,
interior = interior
);
}
polygon([
@ -142,24 +242,58 @@ module gear2d(
}
// Creates an involute spur gear, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// Meshing gears must match in mm_per_tooth, pressure_angle, and twist,
// and be separated by the sum of their pitch radii, which can be found with pitch_radius().
// mm_per_tooth = This is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
// number_of_teeth = Total number of teeth along the rack
// thickness = Thickness of rack in mm (affects each tooth)
// hole_diameter = Diameter of centeral shaft hole.
// Module: gear()
// Description:
// Creates a (potentially helical) involute spur gear.
// The module `gear()` gives an involute spur gear, with reasonable
// defaults for all the parameters. Normally, you should just choose
// the first 4 parameters, and let the rest be default values. The
// module `gear()` gives a gear in the XY plane, centered on the origin,
// with one tooth centered on the positive Y axis. The various functions
// below it take the same parameters, and return various measurements
// for the gear. The most important is `pitch_radius()`, which tells
// how far apart to space gears that are meshing, and `outer_radius()`,
// which gives the size of the region filled by the gear. A gear has
// a "pitch circle", which is an invisible circle that cuts through
// the middle of each tooth (though not the exact center). In order
// for two gears to mesh, their pitch circles should just touch. So
// the distance between their centers should be `pitch_radius()` for
// one, plus `pitch_radius()` for the other, which gives the radii of
// their pitch circles.
// In order for two gears to mesh, they must have the same `mm_per_tooth`
// and `pressure_angle` parameters. `mm_per_tooth` gives the number
// of millimeters of arc around the pitch circle covered by one tooth
// and one space between teeth. The `pressure_angle` controls how flat or
// bulged the sides of the teeth are. Common values include 14.5
// degrees and 20 degrees, and occasionally 25. Though I've seen 28
// recommended for plastic gears. Larger numbers bulge out more, giving
// stronger teeth, so 28 degrees is the default here.
// The ratio of `number_of_teeth` for two meshing gears gives how many
// times one will make a full revolution when the the other makes one
// full revolution. If the two numbers are coprime (i.e. are not
// both divisible by the same number greater than 1), then every tooth
// on one gear will meet every tooth on the other, for more even wear.
// So coprime numbers of teeth are good.
// Arguments:
// mm_per_tooth = This is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
// number_of_teeth = Total number of teeth around the entire perimeter
// thickness = Thickness of gear in mm
// hole_diameter = Diameter of the hole in the center, in mm
// teeth_to_hide = Number of teeth to delete to make this only a fraction of a circle
// pressure_angle = Controls how straight or bulged the tooth sides are. In degrees.
// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
// clearance = Clearance gap at the bottom of the inter-tooth valleys.
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// twist = Teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once
// bevelang = Angle of beveled gear face.
// twist = Teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once.
// slices = Number of vertical layers to divide gear into. Useful for refining gears with `twist`.
// scale = Scale of top of gear compared to bottom. Useful for making crown gears.
// slices = Number of slices to divide gear into. Useful for refining gears with `twist`.
// Example:
// interior = If true, create a mask for difference()ing from something else.
// orient = Orientation of the gear. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the gear. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example: Spur Gear
// gear(mm_per_tooth=5, number_of_teeth=20, thickness=8, hole_diameter=5);
// Example: Beveled Gear
// gear(mm_per_tooth=5, number_of_teeth=20, thickness=10*cos(45), hole_diameter=5, twist=-30, bevelang=45, slices=12, $fa=1, $fs=1);
// gear(mm_per_tooth=5, number_of_teeth=20, thickness=8, hole_diameter=5, $fa=1, $fs=1);
module gear(
mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 11, //total number of teeth around the entire perimeter
@ -171,33 +305,39 @@ module gear(
backlash = 0.0, //gap between two meshing teeth, in the direction along the circumference of the pitch circle
bevelang = 0.0, //angle of bevelled gear face.
twist = undef, //teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once
slices = undef //Number of slices to divide gear into. Useful for refining gears with `twist`.
slices = undef, //Number of slices to divide gear into. Useful for refining gears with `twist`.
interior = false,
orient = ORIENT_Z,
align = V_CENTER
) {
p = pitch_radius(mm_per_tooth, number_of_teeth);
c = outer_radius(mm_per_tooth, number_of_teeth);
r = root_radius(mm_per_tooth, number_of_teeth, clearance);
c = outer_radius(mm_per_tooth, number_of_teeth, clearance, interior);
r = root_radius(mm_per_tooth, number_of_teeth, clearance, interior);
p2 = p - (thickness*tan(bevelang));
difference() {
linear_extrude(height=thickness, center=true, convexity=10, twist=twist, scale=p2/p, slices=slices) {
gear2d(
mm_per_tooth = mm_per_tooth,
number_of_teeth = number_of_teeth,
teeth_to_hide = teeth_to_hide,
pressure_angle = pressure_angle,
clearance = clearance,
backlash = backlash,
bevelang = bevelang
);
}
if (hole_diameter > 0) {
cylinder(h=2*thickness+1, r=hole_diameter/2, center=true);
}
if (bevelang != 0) {
h = (c-r)*sin(bevelang);
translate([0,0,-thickness/2]) {
difference() {
cube([2*c/cos(bevelang),2*c/cos(bevelang),2*h], center=true);
cylinder(h=h, r1=r, r2=c, center=false);
orient_and_align([p, p, thickness], orient, align) {
difference() {
linear_extrude(height=thickness, center=true, convexity=10, twist=twist, scale=p2/p, slices=slices) {
gear2d(
mm_per_tooth = mm_per_tooth,
number_of_teeth = number_of_teeth,
teeth_to_hide = teeth_to_hide,
pressure_angle = pressure_angle,
clearance = clearance,
backlash = backlash,
bevelang = bevelang,
interior = interior
);
}
if (hole_diameter > 0) {
cylinder(h=2*thickness+1, r=hole_diameter/2, center=true);
}
if (bevelang != 0) {
h = (c-r)*sin(bevelang);
translate([0,0,-thickness/2]) {
difference() {
cube([2*c/cos(bevelang),2*c/cos(bevelang),2*h], center=true);
cylinder(h=h, r1=r, r2=c, center=false);
}
}
}
}
@ -205,17 +345,22 @@ module gear(
}
// Creates a rack, which is a straight line with teeth.
// The same as a segment of teeth from an infinite diameter gear.
// The "pitch circle" is a line along the X axis.
// Module: rack()
// Description:
// The module `rack()` gives a rack, which is a bar with teeth. A
// rack can mesh with any gear that has the same `mm_per_tooth` and
// `pressure_angle`.
// Arguments:
// mm_per_tooth = This is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
// number_of_teeth = Total number of teeth along the rack
// thickness = Thickness of rack in mm (affects each tooth)
// height = Height of rack in mm, from tooth top to back of rack.
// pressure_angle = Controls how straight or bulged the tooth sides are. In degrees.
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// orient = Orientation of the rack. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the rack. Use the `V_` constants from `constants.scad`. Default: `V_RIGHT`.
// Example:
// rack(mm_per_tooth=5, number_of_teeth=30, thickness=5, height=5, pressure_angle=20);
// rack(mm_per_tooth=5, number_of_teeth=10, thickness=5, height=5, pressure_angle=20);
module rack(
mm_per_tooth = 5, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 20, //total number of teeth along the rack
@ -223,57 +368,39 @@ module rack(
height = 10, //height of rack in mm, from tooth top to back of rack.
pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees.
backlash = 0.0, //gap between two meshing teeth, in the direction along the circumference of the pitch circle
clearance = undef
clearance = undef,
orient = ORIENT_X,
align = V_RIGHT
) {
a = adendum(mm_per_tooth);
d = dedendum(mm_per_tooth, clearance);
xa = a * sin(pressure_angle);
xd = d * sin(pressure_angle);
linear_extrude(height = thickness, center = true, convexity = 10) {
for (i = [0:number_of_teeth-1] ) {
translate([i*mm_per_tooth,0,0]) {
polygon(
points=[
[-1/2 * mm_per_tooth - 0.01, a-height],
[-1/2 * mm_per_tooth, -d],
[-1/4 * mm_per_tooth + backlash - xd, -d],
[-1/4 * mm_per_tooth + backlash + xa, a],
[ 1/4 * mm_per_tooth - backlash - xa, a],
[ 1/4 * mm_per_tooth - backlash + xd, -d],
[ 1/2 * mm_per_tooth, -d],
[ 1/2 * mm_per_tooth + 0.01, a-height],
]
);
orient_and_align([(number_of_teeth-1)*mm_per_tooth, height, thickness], orient, align, orig_orient=ORIENT_X) {
left((number_of_teeth-1)*mm_per_tooth/2) {
linear_extrude(height = thickness, center = true, convexity = 10) {
for (i = [0:number_of_teeth-1] ) {
translate([i*mm_per_tooth,0,0]) {
polygon(
points=[
[-1/2 * mm_per_tooth - 0.01, a-height],
[-1/2 * mm_per_tooth, -d],
[-1/4 * mm_per_tooth + backlash - xd, -d],
[-1/4 * mm_per_tooth + backlash + xa, a],
[ 1/4 * mm_per_tooth - backlash - xa, a],
[ 1/4 * mm_per_tooth - backlash + xd, -d],
[ 1/2 * mm_per_tooth, -d],
[ 1/2 * mm_per_tooth + 0.01, a-height],
]
);
}
}
}
}
}
}
//These functions let the user find the derived dimensions of the gear.
//A gear fits within a circle of radius outer_radius, and two gears should have
//their centers separated by the sum of their pitch_radius.
function circular_pitch(mm_per_tooth=5) = mm_per_tooth; //tooth density expressed as "circular pitch" in millimeters
function diametral_pitch(mm_per_tooth=5) = PI / mm_per_tooth; //tooth density expressed as "diametral pitch" in teeth per millimeter
function module_value(mm_per_tooth=5) = mm_per_tooth / PI; //tooth density expressed as "module" or "modulus" in millimeters
function adendum (mm_per_tooth=5) = module_value(mm_per_tooth);
function dedendum (mm_per_tooth=5, clearance=undef) = (clearance==undef)? (1.25 * module_value(mm_per_tooth)) : (module_value(mm_per_tooth) + clearance);
function pitch_radius(mm_per_tooth=5, number_of_teeth=11) = mm_per_tooth * number_of_teeth / PI / 2;
//The gear fits entirely within a cylinder of this radius.
function outer_radius(mm_per_tooth=5, number_of_teeth=11)
= pitch_radius(mm_per_tooth, number_of_teeth) + adendum(mm_per_tooth);
// Radius of circle at base of dedendum.
function root_radius(mm_per_tooth=5, number_of_teeth=11, clearance=undef)
= pitch_radius(mm_per_tooth, number_of_teeth) - dedendum(mm_per_tooth, clearance);
// The base circle for involute teeth.
function base_radius(mm_per_tooth=5, number_of_teeth=11, pressure_angle=28)
= pitch_radius(mm_per_tooth, number_of_teeth) * cos(pressure_angle);
//////////////////////////////////////////////////////////////////////////////////////////////
//example gear train.
//Try it with OpenSCAD View/Animate command with 20 steps and 24 FPS.

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Snap-together joiners
// LibFile: joiners.scad
// Snap-together joiners.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/joiners.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,180 +37,421 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
use <transforms.scad>
use <shapes.scad>
include <compat.scad>
include <constants.scad>
module half_joiner_clear(h=20, w=10, a=30, clearance=0)
// Section: Half Joiners
// Module: half_joiner_clear()
// Description:
// Creates a mask to clear an area so that a half_joiner can be placed there.
// Usage:
// half_joiner_clear(h, w, [a], [clearance], [overlap], [orient], [align])
// Arguments:
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// half_joiner_clear(orient=ORIENT_X);
module half_joiner_clear(h=20, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
{
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
difference() {
// Diamonds.
scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
}
// Blunt point of tab.
grid_of(ya=[-(guide_width/2+2), (guide_width/2+2)]) {
cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
}
}
}
//half_joiner_clear();
module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
{
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
difference() {
union() {
// Make base.
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
yspread(overlap, n=overlap>0? 2 : 1) {
difference() {
// Solid backing base.
translate([0,-l/2,0])
cube(size=[w, l, h], center=true);
// Clear diamond for tab
grid_of(xa=[-(w*2/3), (w*2/3)]) {
half_joiner_clear(h=h+0.01, w=w, clearance=slop*2, a=a);
// Diamonds.
scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
}
}
difference() {
// Make tab
scale([w/3-slop*2, dmnd_width/2, dmnd_height/2]) xrot(45)
cube(size=[1,sqrt(2),sqrt(2)], center=true);
// Blunt point of tab.
translate([0,guide_width/2+2,0])
cube(size=[w*0.99,4,guide_size*2], center=true);
}
// Guide ridges.
if (guides == true) {
xspread(w/3-slop*2) {
// Guide ridge.
fwd(0.05/2) {
scale([0.75, 1, 2]) yrot(45)
cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true);
}
// Snap ridge.
scale([0.25, 0.5, 1]) zrot(45)
cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true);
yspread(guide_width+4) {
cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
}
}
}
if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true);
}
}
// Make screwholes, if needed.
if (screwsize != undef) {
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
// Module: half_joiner()
// Usage:
// half_joiner(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
// Description:
// Creates a half_joiner object that can be attached to half_joiner2 object.
// Arguments:
// h = Height of the half_joiner.
// w = Width of the half_joiner.
// l = Length of the backing to the half_joiner.
// a = Overhang angle of the half_joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
// slop = Printer specific slop value to make parts fit more closely.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// half_joiner(screwsize=3, orient=ORIENT_X);
module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
{
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
if ($children > 0) {
difference() {
children();
half_joiner_clear(h=h, w=w, a=a, clearance=0.1, overlap=0.01, orient=orient, align=align);
}
}
render(convexity=12)
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
difference() {
union() {
// Make base.
difference() {
// Solid backing base.
fwd(l/2) cube(size=[w, l, h], center=true);
// Clear diamond for tab
grid3d(xa=[-(w*2/3), (w*2/3)]) {
half_joiner_clear(h=h+0.01, w=w, clearance=slop*2, a=a);
}
}
difference() {
// Make tab
scale([w/3-slop*2, dmnd_width/2, dmnd_height/2]) xrot(45)
cube(size=[1,sqrt(2),sqrt(2)], center=true);
// Blunt point of tab.
back(guide_width/2+2)
cube(size=[w*0.99,4,guide_size*2], center=true);
}
// Guide ridges.
if (guides == true) {
xspread(w/3-slop*2) {
// Guide ridge.
fwd(0.05/2) {
scale([0.75, 1, 2]) yrot(45)
cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true);
}
// Snap ridge.
scale([0.25, 0.5, 1]) zrot(45)
cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true);
}
}
}
// Make screwholes, if needed.
if (screwsize != undef) {
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
}
}
}
}
//half_joiner(screwsize=3);
//half_joiner(screwsize=3, orient=ORIENT_Z, align=V_UP);
module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true)
// Module: half_joiner2()
// Usage:
// half_joiner2(h, w, l, [a], [screwsize], [guides], [orient], [align])
// Description:
// Creates a half_joiner2 object that can be attached to half_joiner object.
// Arguments:
// h = Height of the half_joiner.
// w = Width of the half_joiner.
// l = Length of the backing to the half_joiner.
// a = Overhang angle of the half_joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// half_joiner2(screwsize=3, orient=ORIENT_X);
module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, orient=ORIENT_Y, align=V_CENTER)
{
difference() {
union () {
translate([0,-l/2,0])
cube(size=[w, l, h], center=true);
half_joiner_clear(h=h, w=w, a=a);
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
if ($children > 0) {
difference() {
children();
half_joiner_clear(h=h, w=w, a=a, clearance=0.1, overlap=0.01, orient=orient, align=align);
}
}
// Subtract mated half_joiner.
zrot(180) half_joiner(h=h+0.05, w=w+0.05, l=l+0.05, a=a, screwsize=undef, guides=guides, slop=0.0);
render(convexity=12)
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
difference() {
union () {
fwd(l/2) cube(size=[w, l, h], center=true);
cube([w, guide_width, h], center=true);
}
// Make screwholes, if needed.
if (screwsize != undef) {
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
// Subtract mated half_joiner.
zrot(180) half_joiner(h=h+0.01, w=w+0.01, l=guide_width+0.01, a=a, screwsize=undef, guides=guides, slop=0.0);
// Make screwholes, if needed.
if (screwsize != undef) {
xcyl(r=screwsize*1.1/2, l=w+1, $fn=12);
}
}
}
}
//half_joiner2(screwsize=3);
module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
// Section: Full Joiners
// Module: joiner_clear()
// Description:
// Creates a mask to clear an area so that a joiner can be placed there.
// Usage:
// joiner_clear(h, w, [a], [clearance], [overlap], [orient], [align])
// Arguments:
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// joiner_clear(orient=ORIENT_X);
module joiner_clear(h=40, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
{
union() {
translate([0,0,h/4])
half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
translate([0,0,-h/4])
half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
dmnd_height = h*0.5;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance);
down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01);
}
}
//joiner(screwsize=3);
module joiner_clear(h=40, w=10, a=30, clearance=0)
// Module: joiner()
// Usage:
// joiner(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
// Description:
// Creates a joiner object that can be attached to another joiner object.
// Arguments:
// h = Height of the joiner.
// w = Width of the joiner.
// l = Length of the backing to the joiner.
// a = Overhang angle of the joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
// slop = Printer specific slop value to make parts fit more closely.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// joiner(screwsize=3, orient=ORIENT_X);
// joiner(w=10, l=10, h=40, orient=ORIENT_X) cuboid([10, 10*2, 40], align=V_LEFT);
module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
{
up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, clearance=clearance);
down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, clearance=-0.01);
if ($children > 0) {
difference() {
children();
joiner_clear(h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
}
}
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
}
}
//joiner_clear();
module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
// Section: Full Joiners Pairs/Sets
// Module: joiner_pair_clear()
// Description:
// Creates a mask to clear an area so that a pair of joiners can be placed there.
// Usage:
// joiner_pair_clear(spacing, [n], [h], [w], [a], [clearance], [overlap], [orient], [align])
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// n = Number of joiners (2 by default) to clear for.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// joiner_pair_clear(spacing=50, n=2);
// joiner_pair_clear(spacing=50, n=3);
module joiner_pair_clear(spacing=100, h=40, w=10, a=30, n=2, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
{
yrot_copies([0,180]) {
translate([spacing/2, 0, 0]) {
joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
dmnd_height = h*0.5;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
orient_and_align([spacing+w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
xspread(spacing, n=n) {
joiner_clear(h=h, w=w, a=a, clearance=clearance, overlap=overlap);
}
}
}
//joiner_pair(spacing=100, h=40, w=10, l=10, a=30, screwsize=3, guides=true);
module joiner_pair_clear(spacing=100, h=40, w=10, a=30, clearance=0)
// Module: joiner_pair()
// Usage:
// joiner_pair(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
// Description:
// Creates a joiner_pair object that can be attached to other joiner_pairs .
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiners.
// w = Width of the joiners.
// l = Length of the backing to the joiners.
// a = Overhang angle of the joiners.
// n = Number of joiners in a row. Default: 2
// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
// slop = Printer specific slop value to make parts fit more closely.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// joiner_pair(spacing=50, l=10, orient=ORIENT_X) cuboid([10, 50+10-0.1, 40], align=V_LEFT);
// joiner_pair(spacing=50, l=10, n=2, orient=ORIENT_X);
// joiner_pair(spacing=50, l=10, n=3, alternate=false, orient=ORIENT_X);
// joiner_pair(spacing=50, l=10, n=3, alternate=true, orient=ORIENT_X);
// joiner_pair(spacing=50, l=10, n=3, alternate="alt", orient=ORIENT_X);
module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
{
yrot_copies([0,180]) {
translate([spacing/2, 0, 0]) {
joiner_clear(h=h, w=w, a=a, clearance=clearance);
if ($children > 0) {
difference() {
children();
joiner_pair_clear(spacing=spacing, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
}
}
orient_and_align([spacing+w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
left((n-1)*spacing/2) {
for (i=[0:n-1]) {
right(i*spacing) {
yrot(180 + (alternate? (i*180+(alternate=="alt"?180:0))%360 : 0)) {
joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
}
}
}
}
}
}
//joiner_pair_clear(spacing=100, h=40, w=10, a=30);
module joiner_quad(xspacing=100, yspacing=50, h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
// Section: Full Joiners Quads/Sets
// Module: joiner_quad_clear()
// Description:
// Creates a mask to clear an area so that a pair of joiners can be placed there.
// Usage:
// joiner_quad_clear(spacing, [n], [h], [w], [a], [clearance], [overlap], [orient], [align])
// Arguments:
// spacing1 = Spacing between joiner centers.
// spacing2 = Spacing between back-to-back pairs/sets of joiners.
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// n = Number of joiners in a row. Default: 2
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// joiner_quad_clear(spacing1=50, spacing2=50, n=2);
// joiner_quad_clear(spacing1=50, spacing2=50, n=3);
module joiner_quad_clear(xspacing=undef, yspacing=undef, spacing1=undef, spacing2=undef, n=2, h=40, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y, align=V_CENTER)
{
zrot_copies([0,180]) {
translate([0, yspacing/2, 0]) {
joiner_pair(spacing=xspacing, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
spacing1 = first_defined([spacing1, xspacing, 100]);
spacing2 = first_defined([spacing2, yspacing, 50]);
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
zrot_copies(n=2) {
back(spacing2/2) {
joiner_pair_clear(spacing=spacing1, n=n, h=h, w=w, a=a, clearance=clearance, overlap=overlap);
}
}
}
}
//joiner_quad(xspacing=100, yspacing=50, h=40, w=10, l=10, a=30, screwsize=3, guides=true);
module joiner_quad_clear(xspacing=100, yspacing=50, h=40, w=10, a=30, clearance=0)
// Module: joiner_quad()
// Usage:
// joiner_quad(h, w, l, [a], [screwsize], [guides], [slop], [orient], [align])
// Description:
// Creates a joiner_quad object that can be attached to other joiner_pairs .
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiners.
// w = Width of the joiners.
// l = Length of the backing to the joiners.
// a = Overhang angle of the joiners.
// n = Number of joiners in a row. Default: 2
// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
// slop = Printer specific slop value to make parts fit more closely.
// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the shape by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// joiner_quad(spacing1=50, spacing2=50, l=10, orient=ORIENT_X) cuboid([50, 50+10-0.1, 40]);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=2, orient=ORIENT_X);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=false, orient=ORIENT_X);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=true, orient=ORIENT_X);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate="alt", orient=ORIENT_X);
module joiner_quad(spacing1=undef, spacing2=undef, xspacing=undef, yspacing=undef, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_CENTER)
{
zrot_copies([0,180]) {
translate([0, yspacing/2, 0]) {
joiner_pair_clear(spacing=xspacing, h=h, w=w, a=a, clearance=clearance);
spacing1 = first_defined([spacing1, xspacing, 100]);
spacing2 = first_defined([spacing2, yspacing, 50]);
if ($children > 0) {
difference() {
children();
joiner_quad_clear(spacing1=spacing1, spacing2=spacing2, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
}
}
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
zrot_copies(n=2) {
back(spacing2/2) {
joiner_pair(spacing=spacing1, n=n, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
}
}
}
}
//joiner_quad_clear(xspacing=100, yspacing=50, h=40, w=10, a=30);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Linear Bearings.
// LibFile: linear_bearings.scad
// Linear Bearing clips/holders.
// To use, add these lines to the top of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/linear_bearings.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -35,6 +41,13 @@ include <shapes.scad>
include <metric_screws.scad>
// Section: Functions
// Function: get_lmXuu_bearing_diam()
// Description: Get outside diameter, in mm, of a standard lmXuu bearing.
// Arguments:
// size = Inner size of lmXuu bearing, in mm.
function get_lmXuu_bearing_diam(size) = lookup(size, [
[ 4.0, 8.0],
[ 5.0, 10.0],
@ -56,6 +69,10 @@ function get_lmXuu_bearing_diam(size) = lookup(size, [
]);
// Function: get_lmXuu_bearing_length()
// Description: Get length, in mm, of a standard lmXuu bearing.
// Arguments:
// size = Inner size of lmXuu bearing, in mm.
function get_lmXuu_bearing_length(size) = lookup(size, [
[ 4.0, 12.0],
[ 5.0, 15.0],
@ -77,7 +94,10 @@ function get_lmXuu_bearing_length(size) = lookup(size, [
]);
// Creates a model of a clamp to hold a given linear bearing cartridge.
// Module: linear_bearing_housing()
// Description:
// Creates a model of a clamp to hold a generic linear bearing cartridge.
// Arguments:
// d = Diameter of linear bearing. (Default: 15)
// l = Length of linear bearing. (Default: 24)
// tab = Clamp tab height. (Default: 7)
@ -85,46 +105,56 @@ function get_lmXuu_bearing_length(size) = lookup(size, [
// wall = Wall thickness of clamp housing. (Default: 3)
// gap = Gap in clamp. (Default: 5)
// screwsize = Size of screw to use to tighten clamp. (Default: 3)
// orient = Orientation of the housing. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the housing by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_UP`
// Example:
// linear_bearing_housing(d=19, l=29, wall=2, tab=6, screwsize=2.5);
module linear_bearing_housing(d=15,l=24,tab=7,gap=5,wall=3,tabwall=5,screwsize=3)
module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, orient=ORIENT_X, align=V_UP)
{
od = d+2*wall;
ogap = gap+2*tabwall;
tabh = tab/2+od/2*sqrt(2)-ogap/2;
translate([0,0,od/2]) difference() {
union() {
rotate([0,0,90])
teardrop(r=od/2,h=l);
translate([0,0,tabh])
cube(size=[l,ogap,tab+0.05], center=true);
translate([0,0,-od/4])
cube(size=[l,od,od/2], center=true);
}
rotate([0,0,90])
teardrop(r=d/2,h=l+0.05);
translate([0,0,(d*sqrt(2)+tab)/2])
cube(size=[l+0.05,gap,d+tab], center=true);
translate([0,0,tabh]) {
translate([0,-ogap/2+2-0.05,0])
rotate([90,0,0])
screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10);
translate([0,ogap/2+0.05,0])
rotate([90,0,0])
metric_nut(size=screwsize,hole=false);
orient_and_align([l, od, od], orient, align, orig_orient=ORIENT_X) {
difference() {
union() {
zrot(90) teardrop(r=od/2,h=l);
up(tabh) cube(size=[l,ogap,tab+0.05], center=true);
down(od/4) cube(size=[l,od,od/2], center=true);
}
zrot(90) teardrop(r=d/2,h=l+0.05);
up((d*sqrt(2)+tab)/2)
cube(size=[l+0.05,gap,d+tab], center=true);
up(tabh) {
fwd(ogap/2-2+0.01)
xrot(90) screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10);
back(ogap/2+0.01)
xrot(90) metric_nut(size=screwsize, hole=false);
}
}
}
}
module lmXuu_housing(size=8,tab=7,gap=5,wall=3,tabwall=5,screwsize=3)
// Module: lmXuu_housing()
// Description:
// Creates a model of a clamp to hold a standard sized lmXuu linear bearing cartridge.
// Arguments:
// size = Standard lmXuu inner size.
// tab = Clamp tab height. Default: 7
// tabwall = Clamp Tab thickness. Default: 5
// wall = Wall thickness of clamp housing. Default: 3
// gap = Gap in clamp. Default: 5
// screwsize = Size of screw to use to tighten clamp. Default: 3
// orient = Orientation of the housing. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
// align = Alignment of the housing by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_UP`
// Example:
// lmXuu_housing(size=10, wall=2, tab=6, screwsize=2.5);
module lmXuu_housing(size=8, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, orient=ORIENT_X, align=V_UP)
{
d = get_lmXuu_bearing_diam(size);
l = get_lmXuu_bearing_length(size);
linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize);
linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize, orient=orient, align=align);
}
//lmXuu_housing(size=8);
//lmXuu_housing(size=10);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Masking shapes.
// LibFile: masks.scad
// Masking shapes.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/masks.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -34,215 +40,65 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use <transforms.scad>
use <shapes.scad>
use <math.scad>
include <compat.scad>
include <constants.scad>
module angle_half_pie_mask(
ang=45, h=1,
r=undef, r1=undef, r2=undef,
d=1.0, d1=undef, d2=undef,
) {
r = (r != undef)? r : (d/2);
r1 = (r1 != undef)? r1 : ((d1 != undef)? (d1/2) : r);
r2 = (r2 != undef)? r2 : ((d2 != undef)? (d2/2) : r);
rm = max(r1,r2);
difference() {
cylinder(h=h, r1=r1, r2=r2, center=true);
translate([0, -rm/2, 0])
cube(size=[rm*2+1, rm, h+1], center=true);
zrot(ang) {
translate([0, rm/2, 0]) {
cube(size=[rm*2.1, rm, h+1], center=true);
}
}
}
}
// Section: General Masks
// Creates a pie wedge shape that can be used to mask other shapes.
// You must specify either r or d, or their r1/r2, d1/d2 variants.
// Module: angle_pie_mask()
// Usage:
// angle_pie_mask(r|d, l, ang, [orient], [align], [center]);
// angle_pie_mask(r1|d1, r2|d2, l, ang, [orient], [align], [center]);
// Description:
// Creates a pie wedge shape that can be used to mask other shapes.
// Arguments:
// ang = angle of wedge in degrees.
// h = height of wedge.
// l = height of wedge.
// r = Radius of circle wedge is created from. (optional)
// r1 = Bottom radius of cone that wedge is created from. (optional)
// r2 = Upper radius of cone that wedge is created from. (optional)
// d = Diameter of circle wedge is created from. (optional)
// d1 = Bottom diameter of cone that wedge is created from. (optional)
// d2 = Upper diameter of cone that wedge is created from. (optional)
// Example:
// angle_pie_mask(ang=30, d=100, h=20);
// orient = Orientation of the pie slice. Use the ORIENT_ constants from constants.h. Default: ORIENT_Z.
// align = Alignment of the pie slice. Use the V_ constants from constants.h. Default: V_CENTER.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example(FR):
// angle_pie_mask(ang=30, d=100, l=20);
module angle_pie_mask(
ang=45, h=1,
ang=45, l=undef,
r=undef, r1=undef, r2=undef,
d=1.0, d1=undef, d2=undef,
d=undef, d1=undef, d2=undef,
orient=ORIENT_Z, align=V_CENTER,
h=undef, center=undef
) {
a1 = min(ang, 180.0);
a2 = max(0.0, ang-180.0);
r = (r != undef)? r : (d/2);
r1 = (r1 != undef)? r1 : ((d1 != undef)? (d1/2) : r);
r2 = (r2 != undef)? r2 : ((d2 != undef)? (d2/2) : r);
union() {
angle_half_pie_mask(h=h, r1=r1, r2=r2, ang=a1);
if (a2 > 0.0) {
zrot(180) angle_half_pie_mask(h=h, r1=r1, r2=r2, ang=a2);
}
l = first_defined([l, h, 1]);
r1 = get_radius(r1, r, d1, d, 10);
r2 = get_radius(r2, r, d2, d, 10);
orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
pie_slice(ang=ang, l=l+0.1, r1=r1, r2=r2, align=V_CENTER);
}
}
// Creates a shape that can be used to chamfer a 90 degree edge.
// Difference it from the object to be chamfered. The center of
// the mask object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = Size of chamfer
// orient = Orientation of the cylinder. Use the ORIENT_ constants from constants.h. Default: vertical.
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// cube(50);
// #chamfer_mask(l=50.1, chamfer=10.0, orient=ORIENT_X, align=V_RIGHT);
// }
module chamfer_mask(l=1.0, chamfer=1.0, orient=ORIENT_Z, align=V_ZERO) {
cyl(d=chamfer*2, l=l, align=align, orient=orient, $fn=4);
}
// Creates a shape that can be used to chamfer a 90 degree edge along the Z axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// down(5) cube(10);
// chamfer_mask_z(l=10.1, chamfer=2.0);
// }
module chamfer_mask_z(l=1.0, chamfer=1.0, align=V_ZERO) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Z, align=align);
}
// Creates a shape that can be used to chamfer a 90 degree edge along the Y axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// fwd(5) cube(10);
// chamfer_mask_y(l=10.1, chamfer=2.0);
// }
module chamfer_mask_y(l=1.0, chamfer=1.0, align=V_ZERO) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Y, align=align);
}
// Creates a shape that can be used to chamfer a 90 degree edge along the X axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// left(5) cube(10);
// chamfer_mask_x(l=10.1, chamfer=2.0);
// }
module chamfer_mask_x(l=1.0, chamfer=1.0, align=V_ZERO) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_X, align=align);
}
// Chamfers the edges of a cuboid region containing the given children.
// chamfer = Inset of the chamfer from the edge. (Default: 1)
// size = The size of the rectangular cuboid we want to chamfer.
// edges = Which edges do we want to chamfer. Recommend to use EDGE constants from constants.scad.
// [
// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
// [X+Z+, X-Z+, X-Z-, X+Z-],
// [X+Y+, X-Y+, X-Y-, X+Y-]
// ]
// Example:
// include <BOSL/constants.scad>
// chamfer(chamfer=2, size=[10,40,30], edges=EDGE_BOT_BK + EDGE_TOP_RT + EDGE_TOP_LF) {
// cube(size=[10,40,30], center=true);
// }
module chamfer(chamfer=1, size=[1,1,1], edges=[[0,0,0,0], [1,1,0,0], [0,0,0,0]])
{
eps = 0.1;
x = size[0];
y = size[1];
z = size[2];
lx = x + eps;
ly = y + eps;
lz = z + eps;
difference() {
union() {
children();
}
union() {
if (edges[0][0] > 0)
up(z/2) back(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][1] > 0)
up(z/2) fwd(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][2] > 0)
down(z/2) back(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][3] > 0)
down(z/2) fwd(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[1][0] > 0)
up(z/2) right(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][1] > 0)
up(z/2) left(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][2] > 0)
down(z/2) right(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][3] > 0)
down(z/2) left(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[2][0] > 0)
back(y/2) right(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][1] > 0)
back(y/2) left(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][2] > 0)
fwd(y/2) right(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][3] > 0)
fwd(y/2) left(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
}
}
}
// Create a mask that can be used to bevel/chamfer the end of a cylindrical region.
// Difference it from the end of the region to be chamferred. The center of the mask
// object should align exactly with the center of the end of the cylindrical region
// to be chamferred.
// r = Radius of cylinder to chamfer.
// d = Diameter of cylinder to chamfer. Use instead of r.
// chamfer = Size of the edge chamferred, inset from edge. (Default: 0.25)
// ang = Angle of chamfer in degrees from vertical. (Default: 45)
// from_end = If true, chamfer size is measured from end of cylinder. If false, chamfer is measured outset from the radius of the cylinder. (Default: false)
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: ORIENT_Z.
// Example:
// $fa=2; $fs=2;
// difference() {
// cylinder(r=50, h=100, center=true);
// up(50) !chamfer_cylinder_mask(r=50, chamfer=10);
// }
module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false, orient=ORIENT_Z)
{
r = get_radius(r=r, d=d, dflt=1);
rot(orient) cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, align=V_DOWN);
}
// If passed children, bevels/chamfers and/or rounds/fillets the ends of the
// cylindrical/conical region specified. If passed no children, creates
// a mask to bevel/chamfer and/or fillet the ends of the cylindrical
// region specified. Difference the mask from the region. The center
// of the mask object should align exactly with the center of the
// cylindrical region to be chamferred.
// Module: cylinder_mask()
// Usage: Mask objects
// cylinder_mask(l, r|d, chamfer, [chamfang], [from_end], [circum], [overage], [ends_only], [orient], [align]);
// cylinder_mask(l, r|d, fillet, [circum], [overage], [ends_only], [orient], [align]);
// cylinder_mask(l, r|d, [chamfer1|fillet1], [chamfer2|fillet2], [chamfang1], [chamfang2], [from_end], [circum], [overage], [ends_only], [orient], [align]);
// Usage: Masking operators
// cylinder_mask(l, r|d, chamfer, [chamfang], [from_end], [circum], [overage], [ends_only], [orient], [align]) ...
// cylinder_mask(l, r|d, fillet, [circum], [overage], [ends_only], [orient], [align]) ...
// cylinder_mask(l, r|d, [chamfer1|fillet1], [chamfer2|fillet2], [chamfang1], [chamfang2], [from_end], [circum], [overage], [ends_only], [orient], [align]) ...
// Description:
// If passed children, bevels/chamfers and/or rounds/fillets one or
// both ends of the origin-centered cylindrical region specified. If
// passed no children, creates a mask to bevel/chamfer and/or round/fillet
// one or both ends of the cylindrical region. Difference the mask
// from the region, making sure the center of the mask object is align
// exactly with the center of the cylindrical region to be chamferred.
// Arguments:
// l = Length of the cylindrical/conical region.
// r = Radius of cylindrical region to chamfer.
// r1 = Radius of axis-negative end of the region to chamfer.
@ -264,13 +120,13 @@ module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=fals
// overage = The extra thickness of the mask. Default: `10`.
// ends_only = If true, only mask the ends and not around the middle of the cylinder.
// orient = Orientation. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the region. Use the `V_` constants from `constants.scad`. Default: `V_ZERO`.
// align = Alignment of the region. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// $fa=2; $fs=2;
// difference() {
// cylinder(h=100, r1=60, r2=30, center=true);
// cylinder_mask(l=100, r1=60, r2=30, chamfer=10, from_end=true);
// }
// Example:
// cylinder_mask(l=100, r=50, chamfer1=10, fillet2=10) {
// cube([100,50,100], center=true);
// }
@ -283,7 +139,7 @@ module cylinder_mask(
fillet=undef, fillet1=undef, fillet2=undef,
circum=false, from_end=false,
overage=10, ends_only=false,
orient=ORIENT_Z, align=V_ZERO
orient=ORIENT_Z, align=V_CENTER
) {
r1 = get_radius(r=r, d=d, r1=r1, d1=d1, dflt=1);
r2 = get_radius(r=r, d=d, r1=r2, d1=d2, dflt=1);
@ -325,20 +181,179 @@ module cylinder_mask(
// Create a mask that can be used to bevel/chamfer the end of a cylindrical hole.
// Difference it from the hole to be chamferred. The center of the mask object
// should align exactly with the center of the end of the hole to be chamferred.
// Section: Chamfers
// Module: chamfer_mask()
// Usage:
// chamfer_mask(l, chamfer, [orient], [align], [center]);
// Description:
// Creates a shape that can be used to chamfer a 90 degree edge.
// Difference it from the object to be chamfered. The center of
// the mask object should align exactly with the edge to be chamfered.
// Arguments:
// l = Length of mask.
// chamfer = Size of chamfer
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example:
// difference() {
// cube(50);
// #chamfer_mask(l=50, chamfer=10, orient=ORIENT_X, align=V_RIGHT);
// }
module chamfer_mask(l=1, chamfer=1, orient=ORIENT_Z, align=V_CENTER, center=undef) {
orient_and_align([chamfer, chamfer, l], orient, align, center=center) {
cylinder(d=chamfer*2, h=l+0.1, center=true, $fn=4);
}
}
// Module: chamfer_mask_x()
// Usage:
// chamfer_mask_x(l, chamfer, [align]);
// Description:
// Creates a shape that can be used to chamfer a 90 degree edge along the X axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// Arguments:
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// left(40) cube(80);
// #chamfer_mask_x(l=80, chamfer=20);
// }
module chamfer_mask_x(l=1.0, chamfer=1.0, align=V_CENTER) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_X, align=align);
}
// Module: chamfer_mask_y()
// Usage:
// chamfer_mask_y(l, chamfer, [align]);
// Description:
// Creates a shape that can be used to chamfer a 90 degree edge along the Y axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// Arguments:
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// fwd(40) cube(80);
// right(80) #chamfer_mask_y(l=80, chamfer=20);
// }
module chamfer_mask_y(l=1.0, chamfer=1.0, align=V_CENTER) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Y, align=align);
}
// Module: chamfer_mask_z()
// Usage:
// chamfer_mask_z(l, chamfer, [align]);
// Description:
// Creates a shape that can be used to chamfer a 90 degree edge along the Z axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// Arguments:
// l = Height of mask
// chamfer = size of chamfer
// align = Alignment of the cylinder. Use the V_ constants from constants.h. Default: centered.
// Example:
// difference() {
// down(40) cube(80);
// #chamfer_mask_z(l=80, chamfer=20);
// }
module chamfer_mask_z(l=1.0, chamfer=1.0, align=V_CENTER) {
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Z, align=align);
}
// Module: chamfer()
// Usage:
// chamfer(chamfer, size, [edges]) ...
// Description:
// Chamfers the edges of a cuboid region containing childrem, centered on the origin.
// Arguments:
// chamfer = Inset of the chamfer from the edge. (Default: 1)
// size = The size of the rectangular cuboid we want to chamfer.
// edges = Which edges do we want to chamfer. Recommend to use EDGE constants from constants.scad.
// Description:
// You should use `EDGE` constants from `constants.scad` with the `edge` argument.
// However, if you must handle it raw, the edge ordering is this:
// [
// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
// [X+Z+, X-Z+, X-Z-, X+Z-],
// [X+Y+, X-Y+, X-Y-, X+Y-]
// ]
// Example(FR):
// chamfer(chamfer=2, size=[20,40,30]) {
// cube(size=[20,40,30], center=true);
// }
// Example(FR):
// chamfer(chamfer=2, size=[20,40,30], edges=EDGES_TOP - EDGE_TOP_LF + EDGE_FR_RT) {
// cube(size=[20,40,30], center=true);
// }
module chamfer(chamfer=1, size=[1,1,1], edges=EDGES_ALL)
{
difference() {
children();
difference() {
cube(size, center=true);
cuboid(size+[1,1,1]*0.02, chamfer=chamfer+0.01, edges=edges, trimcorners=true);
}
}
}
// Module: chamfer_cylinder_mask()
// Usage:
// chamfer_cylinder_mask(r|d, chamfer, [ang], [from_end], [orient])
// Description:
// Create a mask that can be used to bevel/chamfer the end of a cylindrical region.
// Difference it from the end of the region to be chamferred. The center of the mask
// object should align exactly with the center of the end of the cylindrical region
// to be chamferred.
// Arguments:
// r = Radius of cylinder to chamfer.
// d = Diameter of cylinder to chamfer. Use instead of r.
// chamfer = Size of the edge chamferred, inset from edge. (Default: 0.25)
// ang = Angle of chamfer in degrees from vertical. (Default: 45)
// from_end = If true, chamfer size is measured from end of cylinder. If false, chamfer is measured outset from the radius of the cylinder. (Default: false)
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: ORIENT_Z.
// Example:
// difference() {
// cylinder(r=50, h=100, center=true);
// up(50) #chamfer_cylinder_mask(r=50, chamfer=10);
// }
module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false, orient=ORIENT_Z)
{
r = get_radius(r=r, d=d, dflt=1);
rot(orient) cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, align=V_DOWN);
}
// Module: chamfer_hole_mask()
// Usage:
// chamfer_hole_mask(r|d, chamfer, [ang], [from_end]);
// Description:
// Create a mask that can be used to bevel/chamfer the end of a cylindrical hole.
// Difference it from the hole to be chamferred. The center of the mask object
// should align exactly with the center of the end of the hole to be chamferred.
// Arguments:
// r = Radius of hole to chamfer.
// d = Diameter of hole to chamfer. Use instead of r.
// chamfer = Size of the chamfer. (Default: 0.25)
// ang = Angle of chamfer in degrees from vertical. (Default: 45)
// from_end = If true, chamfer size is measured from end of hole. If false, chamfer is measured outset from the radius of the hole. (Default: false)
// Example:
// $fa=2; $fs=2;
// difference() {
// cube(100, center=true);
// cylinder(d=50, h=100.1, center=true);
// up(50) chamfer_hole_mask(d=50, chamfer=10);
// up(50) #chamfer_hole_mask(d=50, chamfer=10);
// }
module chamfer_hole_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false)
{
@ -350,114 +365,150 @@ module chamfer_hole_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false)
// Creates a shape that can be used to fillet a vertical 90 degree edge.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the edge to be filletted.
// h = height of vertical mask.
// r = radius of the fillet.
// center = If true, vertically center mask.
// Section: Filleting/Rounding
// Module: fillet_mask()
// Usage:
// fillet_mask(l|h, r, [orient], [align], [center])
// Description:
// Creates a shape that can be used to fillet a vertical 90 degree edge.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the edge to be filletted.
// Arguments:
// l = Length of mask.
// r = Radius of the fillet.
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example:
// difference() {
// cube(size=100, center=false);
// up(50) fillet_mask(h=100.1, r=25.0);
// #fillet_mask(l=100, r=25, orient=ORIENT_Z, align=V_UP);
// }
module fillet_mask(h=1.0, r=1.0, center=true)
module fillet_mask(l=undef, r=1.0, orient=ORIENT_Z, align=V_CENTER, h=undef, center=undef)
{
n = ceil(segs(r)/4)*4;
linear_extrude(height=h, convexity=4, center=center) {
polygon(
points=concat(
[for (a = [ 0:360/n: 90]) [r*cos(a)-r, r*sin(a)-r]],
[for (a = [270:360/n:360]) [r*cos(a)-r, r*sin(a)+r]],
[for (a = [180:360/n:270]) [r*cos(a)+r, r*sin(a)+r]],
[for (a = [ 90:360/n:180]) [r*cos(a)+r, r*sin(a)-r]]
)
);
l = first_defined([l, h, 1]);
sides = quantup(segs(r),4);
orient_and_align([2*r, 2*r, l], orient, align, center=center) {
linear_extrude(height=l+0.1, convexity=4, center=true) {
difference() {
square(2*r, center=true);
xspread(2*r) yspread(2*r) circle(r=r, $fn=sides);
}
}
}
}
module fillet_mask_z(l=1.0, r=1.0) fillet_mask(h=l, r=r, center=true);
module fillet_mask_y(l=1.0, r=1.0) xrot(90) fillet_mask(h=l, r=r, center=true);
module fillet_mask_x(l=1.0, r=1.0) yrot(90) fillet_mask(h=l, r=r, center=true);
// Fillets the edges of a cuboid region containing the given children.
// Module: fillet_mask_x()
// Usage:
// fillet_mask_x(l, r, [align], [center])
// Description:
// Creates a shape that can be used to fillet a 90 degree edge oriented
// along the X axis. Difference it from the object to be filletted.
// The center of the mask object should align exactly with the edge to
// be filletted.
// Arguments:
// l = Length of mask.
// r = Radius of the fillet.
// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example:
// difference() {
// cube(size=100, center=false);
// #fillet_mask_x(l=100, r=25, align=V_RIGHT);
// }
module fillet_mask_x(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_X, align=align);
// Module: fillet_mask_y()
// Usage:
// fillet_mask_y(l, r, [align], [center])
// Description:
// Creates a shape that can be used to fillet a 90 degree edge oriented
// along the Y axis. Difference it from the object to be filletted.
// The center of the mask object should align exactly with the edge to
// be filletted.
// Arguments:
// l = Length of mask.
// r = Radius of the fillet.
// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example:
// difference() {
// cube(size=100, center=false);
// right(100) #fillet_mask_y(l=100, r=25, align=V_BACK);
// }
module fillet_mask_y(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Y, align=align);
// Module: fillet_mask_z()
// Usage:
// fillet_mask_z(l, r, [align], [center])
// Description:
// Creates a shape that can be used to fillet a 90 degree edge oriented
// along the Z axis. Difference it from the object to be filletted.
// The center of the mask object should align exactly with the edge to
// be filletted.
// Arguments:
// l = Length of mask.
// r = Radius of the fillet.
// align = Alignment of the mask. Use the `V_` constants from `constants.h`. Default: centered.
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
// Example:
// difference() {
// cube(size=100, center=false);
// #fillet_mask_z(l=100, r=25, align=V_UP);
// }
module fillet_mask_z(l=1.0, r=1.0, align=V_CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Z, align=align);
// Module: fillet()
// Usage:
// fillet(fillet, size, [edges]) ...
// Description:
// Fillets the edges of a cuboid region containing the given children.
// Arguments:
// fillet = Radius of the fillet. (Default: 1)
// size = The size of the rectangular cuboid we want to chamfer.
// edges = Which edges do we want to chamfer. Recommend to use EDGE constants from constants.scad.
// [
// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
// [X+Z+, X-Z+, X-Z-, X+Z-],
// [X+Y+, X-Y+, X-Y-, X+Y-]
// ]
// Example:
// include <BOSL/constants.scad>
// fillet(fillet=10, size=[50,100,150], edges=EDGES_TOP + EDGES_RIGHT - EDGE_BOT_RT, $fn=24) {
// Description:
// You should use `EDGE` constants from `constants.scad` with the `edge` argument.
// However, if you must handle it raw, the edge ordering is this:
// [
// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
// [X+Z+, X-Z+, X-Z-, X+Z-],
// [X+Y+, X-Y+, X-Y-, X+Y-]
// ]
// Example(FR):
// fillet(fillet=10, size=[50,100,150], $fn=24) {
// cube(size=[50,100,150], center=true);
// }
module fillet(fillet=1, size=[1,1,1], edges=[[0,0,0,0], [1,1,0,0], [0,0,0,0]])
// Example(FR,FlatSpin):
// fillet(fillet=10, size=[50,50,75], edges=EDGES_TOP - EDGE_TOP_LF + EDGE_FR_RT, $fn=24) {
// cube(size=[50,50,75], center=true);
// }
module fillet(fillet=1, size=[1,1,1], edges=EDGES_ALL)
{
eps = 0.1;
x = size[0];
y = size[1];
z = size[2];
lx = x + eps;
ly = y + eps;
lz = z + eps;
rx = x - 2*fillet;
ry = y - 2*fillet;
rz = z - 2*fillet;
majrots = [[0,90,0], [90,0,0], [0,0,0]];
sides = quantup(segs(fillet),4);
sc = 1/cos(180/sides);
difference() {
children();
// Round edges.
for (axis=[0:2], i=[0:3]) {
if (edges[axis][i]>0) {
difference() {
translate(vmul(EDGE_OFFSETS[axis][i], [lx,ly,lz]/2)) {
rotate(majrots[axis]) {
cube([fillet*2, fillet*2, size[axis]+eps], center=true);
}
}
translate(vmul(EDGE_OFFSETS[axis][i], [rx,ry,rz]/2)) {
rotate(majrots[axis]) {
zrot(180/sides) cylinder(h=size[axis]+eps*2, r=fillet*sc, center=true, $fn=sides);
}
}
}
}
}
// Round corners.
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]/2, [lx,ly,lz])) {
cube(fillet*2, center=true);
}
translate(vmul([xa,ya,za]/2, [rx,ry,rz])) {
zrot(180/sides) {
rotate_extrude(convexity=2) {
difference() {
zrot(180/sides) circle(r=fillet*sc*sc, $fn=sides);
left(fillet*2) square(fillet*2*2, center=true);
}
}
}
}
}
}
difference() {
cube(size, center=true);
cuboid(size+[1,1,1]*0.01, fillet=fillet, edges=edges, trimcorners=true);
}
}
}
// Creates a vertical mask that can be used to fillet the edge where two
// face meet, at any arbitrary angle. Difference it from the object to
// be filletted. The center of the mask should align exactly with the
// edge to be filletted.
// Module: fillet_angled_edge_mask()
// Usage:
// fillet_angled_edge_mask(h, r, [ang], [center]);
// Description:
// Creates a vertical mask that can be used to fillet the edge where two
// face meet, at any arbitrary angle. Difference it from the object to
// be filletted. The center of the mask should align exactly with the
// edge to be filletted.
// Arguments:
// h = height of vertical mask.
// r = radius of the fillet.
// ang = angle that the planes meet at.
@ -465,7 +516,7 @@ module fillet(fillet=1, size=[1,1,1], edges=[[0,0,0,0], [1,1,0,0], [0,0,0,0]])
// Example:
// difference() {
// angle_pie_mask(ang=70, h=50, d=100);
// fillet_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32);
// #fillet_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32);
// }
module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
{
@ -487,9 +538,14 @@ module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
}
// Creates a shape that can be used to fillet the corner of an angle.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the point of the corner to be filletted.
// Module: fillet_angled_corner_mask()
// Usage:
// fillet_angled_corner_mask(fillet, ang);
// Description:
// Creates a shape that can be used to fillet the corner of an angle.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the point of the corner to be filletted.
// Arguments:
// fillet = radius of the fillet.
// ang = angle between planes that you need to fillet the corner of.
// Example:
@ -497,7 +553,7 @@ module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
// difference() {
// angle_pie_mask(ang=ang, h=50, r=200);
// up(50/2) {
// fillet_angled_corner_mask(fillet=20, ang=ang);
// #fillet_angled_corner_mask(fillet=20, ang=ang);
// zrot_copies([0, ang]) right(200/2) fillet_mask_x(l=200, r=20);
// }
// fillet_angled_edge_mask(h=51, r=20, ang=ang);
@ -523,70 +579,88 @@ module fillet_angled_corner_mask(fillet=1.0, ang=90)
}
// Creates a shape that you can use to round 90 degree corners on a fillet.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the corner to be filletted.
// Module: fillet_corner_mask()
// Usage:
// fillet_corner_mask(r);
// Description:
// Creates a shape that you can use to round 90 degree corners on a fillet.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the corner to be filletted.
// Arguments:
// r = radius of corner fillet.
// Example:
// $fa=1; $fs=1;
// fillet_corner_mask(r=20.0);
// Example:
// difference() {
// cube(size=[6,10,16], center=true);
// translate([0, 5, 8]) yrot(90) fillet_mask(h=7, r=3);
// translate([3, 0, 8]) xrot(90) fillet_mask(h=11, r=3);
// translate([3, 5, 0]) fillet_mask(h=17, r=3);
// translate([3, 5, 8]) fillet_corner_mask(r=3);
// cube(size=[30, 50, 80], center=true);
// translate([0, 25, 40]) fillet_mask_x(l=31, r=15);
// translate([15, 0, 40]) fillet_mask_y(l=51, r=15);
// translate([15, 25, 0]) fillet_mask_z(l=81, r=15);
// translate([15, 25, 40]) #fillet_corner_mask(r=15);
// }
module fillet_corner_mask(r=1.0)
{
difference() {
cube(size=r*2, center=true);
grid_of(count=[2,2,2], spacing=r*2-0.05) {
sphere(r=r, center=true);
grid3d(n=[2,2,2], spacing=r*2-0.05) {
sphere(r=r);
}
}
}
//!fillet_corner_mask(r=10.0);
// Create a mask that can be used to round the end of a cylinder.
// Difference it from the cylinder to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// cylinder to be filletted.
// Module: fillet_cylinder_mask()
// Usage:
// fillet_cylinder_mask(r, fillet, [xtilt], [ytilt]);
// Description:
// Create a mask that can be used to round the end of a cylinder.
// Difference it from the cylinder to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// cylinder to be filletted.
// Arguments:
// r = radius of cylinder to fillet. (Default: 1.0)
// fillet = radius of the edge filleting. (Default: 0.25)
// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
// Example:
// $fa=2; $fs=2;
// difference() {
// cylinder(r=50, h=100, center=true);
// up(50) fillet_cylinder_mask(r=50, fillet=10, xtilt=30);
// cylinder(r=50, h=50, center=false);
// up(50) #fillet_cylinder_mask(r=50, fillet=10);
// }
// Example:
// difference() {
// cylinder(r=50, h=100, center=false);
// up(75) fillet_cylinder_mask(r=50, fillet=10, xtilt=30);
// }
module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
{
skew_xz(za=xtilt) {
skew_yz(za=ytilt) {
cylinder_mask(l=fillet*3, r=r, fillet2=fillet, ends_only=true, align=V_DOWN);
cylinder_mask(l=fillet*3, r=r, fillet2=fillet, overage=fillet+2*r*sin(max(xtilt,ytilt)), ends_only=true, align=V_DOWN);
}
}
}
// Create a mask that can be used to round the edge of a circular hole.
// Difference it from the hole to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// hole to be filletted.
// Module: fillet_hole_mask()
// Usage:
// fillet_hole_mask(r, fillet, [xtilt], [ytilt]);
// Description:
// Create a mask that can be used to round the edge of a circular hole.
// Difference it from the hole to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// hole to be filletted.
// Arguments:
// r = radius of hole to fillet. (Default: 1.0)
// fillet = radius of the edge filleting. (Default: 0.25)
// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
// Example:
// $fa=2; $fs=2;
// difference() {
// cube([150,150,100], center=true);
// cylinder(r=50, h=100.1, center=true);
// up(50) fillet_hole_mask(r=50, fillet=10, xtilt=0, ytilt=0);
// up(50) #fillet_hole_mask(r=50, fillet=10, xtilt=0, ytilt=0);
// }
module fillet_hole_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
{

1201
math.scad

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Screws, Bolts, and Nuts.
// LibFile: metric_screws.scad
// Screws, Bolts, and Nuts.
// To use, include the following lines at the top of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/metric_screws.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,6 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <constants.scad>
use <transforms.scad>
use <shapes.scad>
use <threading.scad>
@ -39,6 +46,11 @@ use <torx_drive.scad>
use <math.scad>
// Section: Functions
// Function: get_metric_bolt_head_size()
// Description: Returns the diameter of a typical metric bolt's head, based on the bolt `size`.
function get_metric_bolt_head_size(size) = lookup(size, [
[ 3.0, 5.5],
[ 4.0, 7.0],
@ -62,6 +74,8 @@ function get_metric_bolt_head_size(size) = lookup(size, [
]);
// Function: get_metric_bolt_head_height()
// Description: Returns the height of a typical metric bolt's head, based on the bolt `size`.
function get_metric_bolt_head_height(size) = lookup(size, [
[ 1.6, 1.23],
[ 2.0, 1.53],
@ -86,6 +100,8 @@ function get_metric_bolt_head_height(size) = lookup(size, [
]);
// Function: get_metric_socket_cap_diam()
// Description: Returns the diameter of a typical metric socket cap bolt's head, based on the bolt `size`.
function get_metric_socket_cap_diam(size) = lookup(size, [
[ 1.6, 3.0],
[ 2.0, 3.8],
@ -114,6 +130,8 @@ function get_metric_socket_cap_diam(size) = lookup(size, [
]);
// Function: get_metric_socket_cap_height()
// Description: Returns the height of a typical metric socket cap bolt's head, based on the bolt `size`.
function get_metric_socket_cap_height(size) = lookup(size, [
[ 1.6, 1.7],
[ 2.0, 2.0],
@ -142,6 +160,8 @@ function get_metric_socket_cap_height(size) = lookup(size, [
]);
// Function: get_metric_socket_cap_socket_size()
// Description: Returns the diameter of a typical metric socket cap bolt's hex drive socket, based on the bolt `size`.
function get_metric_socket_cap_socket_size(size) = lookup(size, [
[ 1.6, 1.5],
[ 2.0, 1.5],
@ -170,6 +190,8 @@ function get_metric_socket_cap_socket_size(size) = lookup(size, [
]);
// Function: get_metric_socket_cap_socket_depth()
// Description: Returns the depth of a typical metric socket cap bolt's hex drive socket, based on the bolt `size`.
function get_metric_socket_cap_socket_depth(size) = lookup(size, [
[ 1.6, 0.7],
[ 2.0, 1.0],
@ -198,6 +220,8 @@ function get_metric_socket_cap_socket_depth(size) = lookup(size, [
]);
// Function: get_metric_iso_coarse_thread_pitch()
// Description: Returns the ISO metric standard coarse threading pitch for a given bolt `size`.
function get_metric_iso_coarse_thread_pitch(size) = lookup(size, [
[ 1.6, 0.35],
[ 2.0, 0.40],
@ -229,6 +253,8 @@ function get_metric_iso_coarse_thread_pitch(size) = lookup(size, [
]);
// Function: get_metric_iso_fine_thread_pitch()
// Description: Returns the ISO metric standard fine threading pitch for a given bolt `size`.
function get_metric_iso_fine_thread_pitch(size) = lookup(size, [
[ 1.6, 0.35],
[ 2.0, 0.40],
@ -260,6 +286,8 @@ function get_metric_iso_fine_thread_pitch(size) = lookup(size, [
]);
// Function: get_metric_iso_superfine_thread_pitch()
// Description: Returns the ISO metric standard superfine threading pitch for a given bolt `size`.
function get_metric_iso_superfine_thread_pitch(size) = lookup(size, [
[ 1.6, 0.35],
[ 2.0, 0.40],
@ -291,6 +319,8 @@ function get_metric_iso_superfine_thread_pitch(size) = lookup(size, [
]);
// Function: get_metric_jis_thread_pitch()
// Description: Returns the JIS metric standard threading pitch for a given bolt `size`.
function get_metric_jis_thread_pitch(size) = lookup(size, [
[ 2.0, 0.40],
[ 2.5, 0.45],
@ -309,6 +339,8 @@ function get_metric_jis_thread_pitch(size) = lookup(size, [
]);
// Function: get_metric_nut_size()
// Description: Returns the typical metric nut flat-to-flat diameter for a given bolt `size`.
function get_metric_nut_size(size) = lookup(size, [
[ 2.0, 4.0],
[ 2.5, 5.0],
@ -327,6 +359,8 @@ function get_metric_nut_size(size) = lookup(size, [
]);
// Function: get_metric_nut_thickness()
// Description: Returns the typical metric nut thickness for a given bolt `size`.
function get_metric_nut_thickness(size) = lookup(size, [
[ 1.6, 1.3],
[ 2.0, 1.6],
@ -353,53 +387,105 @@ function get_metric_nut_thickness(size) = lookup(size, [
]);
// Makes a very simple screw model, useful for making screwholes.
// Section: Modules
// Module: screw()
// Description:
// Makes a very simple screw model, useful for making screwholes.
// Usage:
// screw(screwsize, screwlen, headsize, headlen, [countersunk], [orient], [align])
// Arguments:
// screwsize = diameter of threaded part of screw.
// screwlen = length of threaded part of screw.
// headsize = diameter of the screw head.
// headlen = length of the screw head.
// countersunk = If true, center from cap's top instead of it's bottom.
// Example:
// orient = Orientation of the screw. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the screw. Use the `V_` constants from `constants.scad` or `"sunken"`, or `"base"`. Default: `"base"`.
// Examples:
// screw(screwsize=3,screwlen=10,headsize=6,headlen=3,countersunk=true);
// screw(screwsize=3,screwlen=10,headsize=6,headlen=3, align="base");
module screw(
screwsize=3,
screwlen=10,
headsize=6,
headlen=3,
pitch=undef,
countersunk=false
countersunk=false,
orient=ORIENT_Z,
align="base"
) {
sides = max(12, segs(screwsize/2));
down(countersunk? headlen-0.01 : 0) {
down(screwlen/2) {
if (pitch == undef) {
cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=sides);
} else {
threaded_rod(d=screwsize, l=screwlen+0.05, pitch=pitch, $fn=sides);
algn = countersunk? ALIGN_NEG : align;
alignments = [
["base", [0,0,-headlen/2+screwlen/2]],
["sunken", [0,0,(headlen+screwlen)/2-0.01]]
];
orient_and_align([headsize, headsize, headlen+screwlen], orient, algn, alignments=alignments) {
down(headlen/2-screwlen/2) {
down(screwlen/2) {
if (pitch == undef) {
cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=sides);
} else {
threaded_rod(d=screwsize, l=screwlen+0.05, pitch=pitch, $fn=sides);
}
}
up(headlen/2) cylinder(r=headsize/2, h=headlen, center=true, $fn=sides*2);
}
up(headlen/2) cylinder(r=headsize/2, h=headlen, center=true, $fn=sides*2);
}
}
// Makes a standard metric screw model.
// size = diameter of threaded part of screw.
// headtype = One of "hex", "pan", "button", "round", "countersunk", "oval", "socket". Default: "socket"
// l = length of screw, except for the head.
// Module: metric_bolt()
// Description:
// Makes a standard metric screw model.
// Arguments:
// size = Diameter of threaded part of screw.
// headtype = One of `"hex"`, `"pan"`, `"button"`, `"round"`, `"countersunk"`, `"oval"`, `"socket`". Default: `"socket"`
// l = Length of screw, except for the head.
// shank = Length of unthreaded portion of the shaft.
// pitch = If given, render threads of the given pitch. If 0, then no threads. Overrides coarse argument.
// details = If true model should be rendered with extra details. (Default: false)
// coarse = If true, make coarse threads instead of fine threads. Default = true
// flange = radius of flange beyond the head. Default = 0 (no flange)
// flange = Radius of flange beyond the head. Default = 0 (no flange)
// phillips = If given, the size of the phillips drive hole to add. (ie: "#1", "#2", or "#3")
// torx = If given, the size of the torx drive hole to add. (ie: 10, 20, 30, etc.)
// Examples:
// metric_bolt(headtype="pan", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="countersunk", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="socket", size=10, l=15, flange=4, coarse=false, shank=5, details=true);
// metric_bolt(headtype="hex", size=10, l=15, flange=4, coarse=false, shank=5, details=true, phillips="#2");
// metric_bolt(headtype="hex", size=10, l=15, flange=4, coarse=false, shank=5, details=true, torx=50);
// orient = Orientation of the bolt. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the bolt. Use the `V_` constants from `constants.scad` or `"sunken"`, `"base"`, or `"shank"`. Default: `"base"`.
// Example: Bolt Head Types
// ydistribute(40) {
// xdistribute(30) {
// // Front Row, Left to Right
// metric_bolt(headtype="pan", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="button", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="round", size=10, l=15, details=true, phillips="#2");
// }
// xdistribute(30) {
// // Back Row, Left to Right
// metric_bolt(headtype="socket", size=10, l=15, details=true);
// metric_bolt(headtype="hex", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="countersunk", size=10, l=15, details=true, phillips="#2");
// metric_bolt(headtype="oval", size=10, l=15, details=true, phillips="#2");
// }
// }
// Example: Details
// metric_bolt(size=10, l=15, details=true, $fn=32);
// Example: No Details Except Threads
// metric_bolt(size=10, l=15);
// Example: No Details, No Threads
// metric_bolt(size=10, l=15, pitch=0);
// Example: Fine Threads
// metric_bolt(size=10, l=15, coarse=false);
// Example: Flange
// metric_bolt(size=10, l=15, flange=5);
// Example: Shank
// metric_bolt(size=10, l=25, shank=10);
// Example: Hex Head with Phillips
// metric_bolt(headtype="hex", size=10, l=15, phillips="#2");
// Example: Hex Head with Torx
// metric_bolt(headtype="hex", size=10, l=15, torx=50);
module metric_bolt(
headtype="socket",
size=3,
@ -410,7 +496,9 @@ module metric_bolt(
coarse=true,
phillips=undef,
torx=undef,
flange=0
flange=0,
orient=ORIENT_Z,
align="base"
) {
D = headtype != "hex"?
get_metric_socket_cap_diam(size) :
@ -427,122 +515,157 @@ module metric_bolt(
bevtop = (tcirc-D)/2;
bevbot = P/2;
//algn = (headtype == "countersunk" || headtype == "oval")? (D-size)/2 : 0;
headlen = (
(headtype == "pan" || headtype == "round" || headtype == "button")? H*0.75 :
(headtype == "countersunk")? (D-size)/2 :
(headtype == "oval")? ((D-size)/2 + D/2/3) :
H
);
base = l/2 - headlen/2;
sunklen = (
(headtype == "oval")? (D-size)/2 :
headlen-0.001
);
alignments = [
["sunken", [0,0,base+sunklen]],
["base", [0,0,base]],
["shank", [0,0,base-shank]]
];
color("silver")
down(headtype == "countersunk" || headtype == "oval"? (D-size)/2 : 0) {
difference() {
union() {
// Head
if (headtype == "hex") {
difference() {
cylinder(d=tcirc, h=H, center=false, $fn=6);
orient_and_align([D+flange, D+flange, headlen+l], orient, align, alignments=alignments) {
up(base) {
difference() {
union() {
// Head
if (headtype == "hex") {
difference() {
cylinder(d=tcirc, h=H, center=false, $fn=6);
// Bevel hex nut top
if (details) {
up(H-bevtop) {
// Bevel hex nut top
if (details) {
up(H-bevtop) {
difference() {
upcube([tcirc+1, tcirc+1, bevtop+0.5]);
down(0.01) cylinder(d1=tcirc, d2=tcirc-bevtop*2, h=bevtop+0.02, center=false);
}
}
}
}
} else if (headtype == "socket") {
sockw = get_metric_socket_cap_socket_size(size);
sockd = get_metric_socket_cap_socket_depth(size);
difference() {
cylinder(d=D, h=H, center=false);
up(H-sockd) cylinder(h=sockd+0.1, d=sockw/cos(30), center=false, $fn=6);
if (details) {
kcnt = 36;
zring(n=kcnt, r=D/2) up(H/3) upcube([PI*D/kcnt/2, PI*D/kcnt/2, H]);
}
}
} else if (headtype == "pan") {
cyl(l=H*0.75, d=D, fillet2=H*0.75/2, align=V_UP);
} else if (headtype == "round") {
top_half() zscale(H*0.75/D*2) sphere(d=D);
} else if (headtype == "button") {
up(H*0.75/3) top_half() zscale(H*0.75*2/3/D*2) sphere(d=D);
cylinder(d=D, h=H*0.75/3+0.01, center=false);
} else if (headtype == "countersunk") {
cylinder(h=(D-size)/2, d1=size, d2=D, center=false);
} else if (headtype == "oval") {
up((D-size)/2) top_half() zscale(0.333) sphere(d=D);
cylinder(h=(D-size)/2, d1=size, d2=D, center=false);
}
// Flange
if (flange>0) {
up(headtype == "countersunk" || headtype == "oval"? (D-size)/2 : 0) {
cylinder(d=D+flange, h=H/8, center=false);
up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false);
}
}
// Unthreaded Shank
if (tlen < l) {
down(l-tlen) cylinder(d=size, h=l-tlen+0.05, center=false, $fn=sides);
}
// Threads
down(l) {
difference() {
up(tlen/2+0.05) {
if (tlen > 0) {
if (P > 0) {
threaded_rod(d=size, l=tlen+0.05, pitch=P, $fn=sides);
} else {
cylinder(d=size, h=tlen+0.05, $fn=sides, center=true);
}
}
}
// Bevel bottom end of threads
if (details) {
difference() {
upcube([tcirc+1, tcirc+1, bevtop+0.5]);
down(0.01) cylinder(d1=tcirc, d2=tcirc-bevtop*2, h=bevtop+0.02, center=false);
down(0.5) upcube([size+1, size+1, bevbot+0.5]);
cylinder(d1=size-bevbot*2, d2=size, h=bevbot+0.01, center=false);
}
}
}
}
} else if (headtype == "socket") {
sockw = get_metric_socket_cap_socket_size(size);
sockd = get_metric_socket_cap_socket_depth(size);
difference() {
cylinder(d=D, h=H, center=false);
up(H-sockd) cylinder(h=sockd+0.1, d=sockw/cos(30), center=false, $fn=6);
if (details) {
kcnt = 36;
zring(n=kcnt, r=D/2) up(H/3) upcube([PI*D/kcnt/2, PI*D/kcnt/2, H]);
}
}
} else if (headtype == "pan") {
top_half() rcylinder(h=H*0.75*2, d=D, fillet=H/2, center=true);
} else if (headtype == "round") {
top_half() zscale(H*0.75/D*2) sphere(d=D);
} else if (headtype == "button") {
up(H*0.75/3) top_half() zscale(H*0.75*2/3/D*2) sphere(d=D);
cylinder(d=D, h=H*0.75/3+0.01, center=false);
} else if (headtype == "countersunk") {
cylinder(h=(D-size)/2, d1=size, d2=D, center=false);
} else if (headtype == "oval") {
up((D-size)/2) top_half() zscale(0.333) sphere(d=D);
cylinder(h=(D-size)/2, d1=size, d2=D, center=false);
}
// Flange
if (flange>0) {
up(headtype == "countersunk" || headtype == "oval"? (D-size)/2 : 0) {
cylinder(d=D+flange, h=H/8, center=false);
up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false);
// Phillips drive hole
if (headtype != "socket" && phillips != undef) {
down(headtype != "hex"? H/6 : 0) {
phillips_drive(size=phillips, shaft=D);
}
}
// Unthreaded Shank
if (tlen < l) {
down(l-tlen) cylinder(d=size, h=l-tlen+0.05, center=false, $fn=sides);
// Torx drive hole
if (headtype != "socket" && torx != undef) {
up(1) torx_drive(size=torx, l=H+0.1, center=false);
}
// Threads
down(l) {
difference() {
up(tlen/2+0.05) {
if (tlen > 0) {
if (P > 0) {
threaded_rod(d=size, l=tlen+0.05, pitch=P, $fn=sides);
} else {
cylinder(d=size, h=tlen+0.05, $fn=sides, center=true);
}
}
}
// Bevel bottom end of threads
if (details) {
difference() {
down(0.5) upcube([size+1, size+1, bevbot+0.5]);
cylinder(d1=size-bevbot*2, d2=size, h=bevbot+0.01, center=false);
}
}
}
}
}
// Phillips drive hole
if (headtype != "socket" && phillips != undef) {
down(headtype != "hex"? H/6 : 0) {
phillips_drive(size=phillips, shaft=D);
}
}
// Torx drive hole
if (headtype != "socket" && torx != undef) {
up(1) torx_drive(size=torx, l=H+0.1, center=false);
}
}
}
}
// Makes a model of a standard nut for a standard metric screw.
// Module: metric_nut()
// Description:
// Makes a model of a standard nut for a standard metric screw.
// Arguments:
// size = standard metric screw size in mm. (Default: 3)
// hole = include the hole in the nut. (Default: true)
// pitch = pitch of threads in the hole. No threads if not given.
// flange = radius of flange beyond the head. Default = 0 (no flange)
// details = true if model should be rendered with extra details. (Default: false)
// center = If true, center the nut at the origin, otherwise on top of the XY plane. Default = false.
// Example:
// metric_nut(size=6, hole=false);
// metric_nut(size=8, hole=true);
// metric_nut(size=6, hole=true, pitch=1, details=true, center=true);
// metric_nut(size=8, hole=true, pitch=1, details=true, flange=3, center=true);
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_UP`.
// center = If true, centers the nut at the origin. If false, sits on top of XY plane. Overrides `align` if given.
// Example: No details, No Hole. Useful for a mask.
// metric_nut(size=10, hole=false);
// Example: Hole, with No Threads
// metric_nut(size=10, hole=true);
// Example: Threads
// metric_nut(size=10, hole=true, pitch=1.5);
// Example: Details
// metric_nut(size=10, hole=true, pitch=1.5, details=true);
// Example: Centered
// metric_nut(size=10, hole=true, pitch=1.5, details=true, center=true);
// Example: Flange
// metric_nut(size=10, hole=true, pitch=1.5, flange=3, details=true);
module metric_nut(
size=3,
hole=true,
pitch=undef,
details=false,
flange=0,
center=false
center=undef,
orient=ORIENT_Z,
align=V_UP
) {
H = get_metric_nut_thickness(size);
D = get_metric_nut_size(size);
@ -550,9 +673,9 @@ module metric_nut(
nutfn = max(12, segs(D/2));
dcirc = D/cos(30);
bevtop = (dcirc - D)/2;
offset = (center == true)? 0 : H/2;
color("silver")
up(offset) {
orient_and_align([dcirc+flange, dcirc+flange, H], orient, align, center) {
difference() {
union() {
difference() {

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Masks and models for NEMA stepper motors.
// LibFile: nema_steppers.scad
// Masks and models for NEMA stepper motors.
// To use, add these lines to the top of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/nema_steppers.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -30,11 +36,20 @@ 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 <transforms.scad>
include <shapes.scad>
include <math.scad>
include <constants.scad>
use <transforms.scad>
use <shapes.scad>
use <math.scad>
use <compat.scad>
// Section: Functions
// Function: nema_motor_width()
// Description: Gets width of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_width(size) = lookup(size, [
[11.0, 28.2],
[14.0, 35.2],
@ -43,6 +58,11 @@ function nema_motor_width(size) = lookup(size, [
[34.0, 86.0],
]);
// Function: nema_motor_plinth_height()
// Description: Gets plinth height of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_plinth_height(size) = lookup(size, [
[11.0, 1.5],
[14.0, 2.0],
@ -51,6 +71,11 @@ function nema_motor_plinth_height(size) = lookup(size, [
[34.0, 2.03],
]);
// Function: nema_motor_plinth_diam()
// Description: Gets plinth diameter of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_plinth_diam(size) = lookup(size, [
[11.0, 22.0],
[14.0, 22.0],
@ -59,6 +84,11 @@ function nema_motor_plinth_diam(size) = lookup(size, [
[34.0, 73.0],
]);
// Function: nema_motor_screw_spacing()
// Description: Gets screw spacing of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_screw_spacing(size) = lookup(size, [
[11.0, 23.11],
[14.0, 26.0],
@ -67,6 +97,11 @@ function nema_motor_screw_spacing(size) = lookup(size, [
[34.0, 69.6],
]);
// Function: nema_motor_screw_size()
// Description: Gets mount screw size of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_screw_size(size) = lookup(size, [
[11.0, 2.6],
[14.0, 3.0],
@ -75,6 +110,11 @@ function nema_motor_screw_size(size) = lookup(size, [
[34.0, 5.5],
]);
// Function: nema_motor_screw_depth()
// Description: Gets mount screwhole depth of NEMA motor of given standard size.
// Arguments:
// size = The standard NEMA motor size.
function nema_motor_screw_depth(size) = lookup(size, [
[11.0, 3.0],
[14.0, 4.5],
@ -84,7 +124,20 @@ function nema_motor_screw_depth(size) = lookup(size, [
]);
module nema11_stepper(h=24, shaft=5, shaft_len=20)
// Section: Motor Models
// Module: nema11_stepper()
// Description: Creates a model of a NEMA 11 stepper motor.
// Arguments:
// h = Length of motor body. Default: 24mm
// shaft = Shaft diameter. Default: 5mm
// shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 20mm
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_DOWN`.
// Example:
// nema11_stepper();
module nema11_stepper(h=24, shaft=5, shaft_len=20, orient=ORIENT_Z, align=V_DOWN)
{
size = 11;
motor_width = nema_motor_width(size);
@ -94,30 +147,39 @@ module nema11_stepper(h=24, shaft=5, shaft_len=20)
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=V_DOWN) {
difference() {
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=V_DOWN);
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
cyl(r=screw_size/2, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
}
color([0.6, 0.6, 0.6]) {
difference() {
cylinder(h=plinth_height, d=plinth_diam);
cyl(h=plinth_height*3, d=shaft+0.75);
}
}
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
down(screw_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
//!nema11_stepper();
module nema14_stepper(h=24, shaft=5, shaft_len=24)
// Module: nema14_stepper()
// Description: Creates a model of a NEMA 14 stepper motor.
// Arguments:
// h = Length of motor body. Default: 24mm
// shaft = Shaft diameter. Default: 5mm
// shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 24mm
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_DOWN`.
// Example:
// nema14_stepper();
module nema14_stepper(h=24, shaft=5, shaft_len=24, orient=ORIENT_Z, align=V_DOWN)
{
size = 14;
motor_width = nema_motor_width(size);
@ -127,30 +189,39 @@ module nema14_stepper(h=24, shaft=5, shaft_len=24)
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=V_DOWN) {
difference() {
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=V_DOWN);
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
}
color([0.6, 0.6, 0.6]) {
difference() {
cylinder(h=plinth_height, d=plinth_diam);
cyl(h=plinth_height*3, d=shaft+0.75);
}
}
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
down(screw_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
cyl(h=shaft_len, d=shaft, align=V_UP, $fn=max(12,segs(shaft/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
//!nema14_stepper();
module nema17_stepper(h=34, shaft=5, shaft_len=20)
// Module: nema17_stepper()
// Description: Creates a model of a NEMA 17 stepper motor.
// Arguments:
// h = Length of motor body. Default: 34mm
// shaft = Shaft diameter. Default: 5mm
// shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 20mm
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_DOWN`.
// Example:
// nema17_stepper();
module nema17_stepper(h=34, shaft=5, shaft_len=20, orient=ORIENT_Z, align=V_DOWN)
{
size = 17;
motor_width = nema_motor_width(size);
@ -160,49 +231,57 @@ module nema17_stepper(h=34, shaft=5, shaft_len=20)
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
down(h/2) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=V_DOWN) {
difference() {
color([0.4, 0.4, 0.4])
cuboid([motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=V_DOWN);
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
}
color([0.6, 0.6, 0.6]) {
difference() {
cylinder(h=plinth_height, d=plinth_diam);
cyl(h=plinth_height*3, d=shaft+0.75);
}
}
color("silver")
xspread(screw_spacing)
yspread(screw_spacing)
down(screw_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
}
color([0.4, 0.4, 0.4])
up(plinth_height/2)
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color([0.9, 0.9, 0.9]) {
down(h-motor_width/12) {
fwd(motor_width/2+motor_width/24/2-0.1) {
difference() {
cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true);
xrot(90) {
cylinder(d=motor_width/8-2, h=motor_width/6, center=true, $fn=12);
color([0.9, 0.9, 0.9]) {
down(h-motor_width/12) {
fwd(motor_width/2+motor_width/24/2-0.1) {
difference() {
cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true);
cyl(d=motor_width/8-2, h=motor_width/6, orient=ORIENT_Y, $fn=12);
}
}
}
}
color("silver") {
difference() {
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
up(shaft_len/2+1) {
right(shaft-0.75) {
cube([shaft, shaft, shaft_len], center=true);
}
}
}
}
}
color("silver") {
difference() {
cylinder(h=shaft_len, r=shaft/2, $fn=max(12,segs(shaft/2)));
up(shaft_len/2+1) {
right(shaft_len/2+shaft/2-0.5) {
cube(shaft_len, center=true);
}
}
}
}
}
//!nema17_stepper();
module nema23_stepper(h=50, shaft=6.35, shaft_len=25)
// Module: nema23_stepper()
// Description: Creates a model of a NEMA 23 stepper motor.
// Arguments:
// h = Length of motor body. Default: 50mm
// shaft = Shaft diameter. Default: 6.35mm
// shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 25mm
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_DOWN`.
// Example:
// nema23_stepper();
module nema23_stepper(h=50, shaft=6.35, shaft_len=25, orient=ORIENT_Z, align=V_DOWN)
{
size = 23;
motor_width = nema_motor_width(size);
@ -213,37 +292,41 @@ module nema23_stepper(h=50, shaft=6.35, shaft_len=25)
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
difference() {
union() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=V_DOWN) {
difference() {
union() {
color([0.4, 0.4, 0.4])
cuboid([motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=V_DOWN);
color([0.4, 0.4, 0.4])
cylinder(h=plinth_height, d=plinth_diam);
color("silver")
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
color([0.4, 0.4, 0.4]) {
xspread(screw_spacing) {
yspread(screw_spacing) {
down(screw_depth/2)
cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth+h/2)
cube(size=[screw_inset, screw_inset, h], center=true);
color([0.4, 0.4, 0.4]) {
xspread(screw_spacing) {
yspread(screw_spacing) {
cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2)));
down(screw_depth) cuboid([screw_inset, screw_inset, h], align=V_DOWN);
}
}
}
}
}
}
//!nema23_stepper();
module nema34_stepper(h=75, shaft=12.7, shaft_len=32)
// Module: nema34_stepper()
// Description: Creates a model of a NEMA 34 stepper motor.
// Arguments:
// h = Length of motor body. Default: 75mm
// shaft = Shaft diameter. Default: 12.7mm
// shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 32mm
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_DOWN`.
// Example:
// nema34_stepper();
module nema34_stepper(h=75, shaft=12.7, shaft_len=32, orient=ORIENT_Z, align=V_DOWN)
{
size = 34;
motor_width = nema_motor_width(size);
@ -254,67 +337,195 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32)
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
difference() {
union() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=V_DOWN) {
difference() {
union() {
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=V_DOWN);
color([0.4, 0.4, 0.4])
cylinder(h=plinth_height, d=plinth_diam);
color("silver")
cylinder(h=shaft_len, d=shaft, $fn=max(24,segs(shaft/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(24,segs(shaft/2)));
}
color([0.4, 0.4, 0.4]) {
xspread(screw_spacing) {
yspread(screw_spacing) {
down(screw_depth/2)
cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth+h/2)
cube(size=[screw_inset, screw_inset, h], center=true);
color([0.4, 0.4, 0.4]) {
xspread(screw_spacing) {
yspread(screw_spacing) {
cylinder(d=screw_size, h=screw_depth*3, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth) downcube([screw_inset, screw_inset, h]);
}
}
}
}
}
}
//!nema34_stepper();
module nema17_mount_holes(depth=5, l=5, slop=printer_slop)
// Section: Masking Modules
// Module: nema_mount_holes()
// Description: Creates a mask to use when making standard NEMA stepper motor mounts.
// Arguments:
// size = The standard NEMA motor size to make a mount for.
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema_mount_holes(size=14, depth=5, l=5);
// Example:
// nema_mount_holes(size=17, depth=5, l=5);
// Example:
// nema_mount_holes(size=17, depth=5, l=0);
module nema_mount_holes(size=17, depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
size = 17;
motor_width = nema_motor_width(size);
plinth_diam = nema_motor_plinth_diam(size)+slop;
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size)+slop;
union() {
xspread(screw_spacing) {
yspread(screw_spacing) {
if (l>0) {
union() {
yspread(l) cylinder(h=depth, d=screw_size, center=true, $fn=max(8,segs(screw_size/2)));
cube([screw_size, l, depth], center=true);
orient_and_align([motor_width, motor_width, l], orient, align) {
union() {
xspread(screw_spacing) {
yspread(screw_spacing) {
if (l>0) {
union() {
yspread(l) cyl(h=depth, d=screw_size, $fn=max(8,segs(screw_size/2)));
cube([screw_size, l, depth], center=true);
}
} else {
cyl(h=depth, d=screw_size, $fn=max(8,segs(screw_size/2)));
}
} else {
cylinder(h=depth, d=screw_size, center=true, $fn=max(8,segs(screw_size/2)));
}
}
}
}
if (l>0) {
union () {
yspread(l) cylinder(h=depth, d=plinth_diam, center=true);
cube([plinth_diam, l, depth], center=true);
if (l>0) {
union () {
yspread(l) cyl(h=depth, d=plinth_diam);
cube([plinth_diam, l, depth], center=true);
}
} else {
cyl(h=depth, d=plinth_diam);
}
} else {
cylinder(h=depth, d=plinth_diam, center=true);
}
}
//!nema17_mount_holes(depth=5, l=5);
// Module: nema11_mount_holes()
// Description: Creates a mask to use when making NEMA 11 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema11_mount_holes(depth=5, l=5);
// Example:
// nema11_mount_holes(depth=5, l=0);
module nema11_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=11, depth=depth, l=l, slop=slop, orient=orient, align=align);
}
// Module: nema14_mount_holes()
// Description: Creates a mask to use when making NEMA 14 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema14_mount_holes(depth=5, l=5);
// Example:
// nema14_mount_holes(depth=5, l=0);
module nema14_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=14, depth=depth, l=l, slop=slop, orient=orient, align=align);
}
// Module: nema17_mount_holes()
// Description: Creates a mask to use when making NEMA 17 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema17_mount_holes(depth=5, l=5);
// Example:
// nema17_mount_holes(depth=5, l=0);
module nema17_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=17, depth=depth, l=l, slop=slop, orient=orient, align=align);
}
// Module: nema23_mount_holes()
// Description: Creates a mask to use when making NEMA 23 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema23_mount_holes(depth=5, l=5);
// Example:
// nema23_mount_holes(depth=5, l=0);
module nema23_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=23, depth=depth, l=l, slop=slop, orient=orient, align=align);
}
// Module: nema34_mount_holes()
// Description: Creates a mask to use when making NEMA 34 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema34_mount_holes(depth=5, l=5);
// Example:
// nema34_mount_holes(depth=5, l=0);
module nema34_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align);
}
// Module: nema34_mount_holes()
// Description: Creates a mask to use when making NEMA 34 stepper motor mounts.
// Arguments:
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// slop = The printer-specific slop value to make parts fit just right. Default: `PRINTER_SLOP`
// orient = Orientation of the stepper. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the stepper. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Example:
// nema34_mount_holes(depth=5, l=5);
// Example:
// nema34_mount_holes(depth=5, l=0);
module nema34_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=V_CENTER)
{
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align);
}

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// 2D and Bezier Stuff.
// LibFile: paths.scad
// Polylines, polygons and paths.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/paths.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,78 +37,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
include <math.scad>
include <quaternions.scad>
include <triangulation.scad>
include <constants.scad>
use <transforms.scad>
use <math.scad>
use <quaternions.scad>
use <triangulation.scad>
// Creates a 2D polygon circle, modulated by one or more superimposed
// sine waves.
// r = radius of the base circle.
// sines = array of [amplitude, frequency] pairs, where the frequency is the
// number of times the cycle repeats around the circle.
// Example:
// modulated_circle(r=40, sines=[[3, 11], [1, 31]], $fn=6);
module modulated_circle(r=40, sines=[10])
{
freqs = len(sines)>0? [for (i=sines) i[1]] : [5];
points = [
for (a = [0 : (360/segs(r)/max(freqs)) : 360])
let(nr=r+sum_of_sines(a,sines)) [nr*cos(a), nr*sin(a)]
];
polygon(points);
}
// Section: Functions
// Extrudes a 2D shape between the points pt1 and pt2.
// Takes as children a set of 2D shapes to extrude.
// pt1 = starting point of extrusion.
// pt2 = ending point of extrusion.
// convexity = max number of times a line could intersect a wall of the 2D shape being extruded.
// twist = number of degrees to twist the 2D shape over the entire extrusion length.
// scale = scale multiplier for end of extrusion compared the start.
// slices = Number of slices along the extrusion to break the extrusion into. Useful for refining `twist` extrusions.
// Example:
// extrude_from_to([0,0,0], [10,20,30], convexity=4, twist=360, scale=3.0, slices=40) {
// xspread(3) circle(3, $fn=32);
// }
module extrude_from_to(pt1, pt2, convexity=undef, twist=undef, scale=undef, slices=undef) {
rtp = xyz_to_spherical(pt2-pt1);
translate(pt1) {
rotate([0, rtp[2], rtp[1]]) {
linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) {
children();
}
}
}
}
// Similar to linear_extrude(), except the result is a hollow shell.
// wall = thickness of shell wall.
// height = height of extrusion.
// twist = degrees of twist, from bottom to top.
// slices = how many slices to use when making extrusion.
// Example:
// extrude_2d_hollow(wall=2, height=100, twist=90, slices=50)
// circle(r=40, center=true, $fn=6);
module extrude_2d_hollow(wall=2, height=50, twist=90, slices=60)
{
linear_extrude(height=height, twist=twist, slices=slices) {
difference() {
children();
offset(r=-wall) {
children();
}
}
}
}
// Takes a 2D polyline and removes uneccessary collinear points.
function simplify2d_path(path) = concat(
// Function: simplify2d_path()
// Description:
// Takes a 2D polyline and removes unnecessary collinear points.
// Usage:
// simplify2d_path(path, [eps])
// Arguments:
// path = A list of 2D path points.
// eps = Largest angle delta between segments to count as colinear. Default: 1e-6
function simplify2d_path(path, eps=1e-6) = concat(
[path[0]],
[
for (
@ -110,14 +63,21 @@ function simplify2d_path(path) = concat(
) let (
v1 = path[i] - path[i-1],
v2 = path[i+1] - path[i-1]
) if (abs(cross(v1,v2)) > 1e-6) path[i]
) if (abs(cross(v1,v2)) > eps) path[i]
],
[path[len(path)-1]]
);
// Takes a 3D polyline and removes uneccessary collinear points.
function simplify3d_path(path) = concat(
// Function: simplify3d_path()
// Description:
// Takes a 3D polyline and removes unnecessary collinear points.
// Usage:
// simplify3d_path(path, [eps])
// Arguments:
// path = A list of 3D path points.
// eps = Largest angle delta between segments to count as colinear. Default: 1e-6
function simplify3d_path(path, eps=1e-6) = concat(
[path[0]],
[
for (
@ -125,67 +85,75 @@ function simplify3d_path(path) = concat(
) let (
v1 = path[i] - path[i-1],
v2 = path[i+1] - path[i-1]
) if (vector3d_angle(v1,v2) > 1e-6) path[i]
) if (vector3d_angle(v1,v2) > eps) path[i]
],
[path[len(path)-1]]
);
// Takes a closed 2D polyline path, centered on the XY plane, and
// extrudes it along a 3D spiral path of a given radius, height and twist.
// polyline = Array of points of a polyline path, to be extruded.
// h = height of the spiral to extrude along.
// r = radius of the spiral to extrude along.
// twist = number of degrees of rotation to spiral up along height.
// Example:
// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// extrude_2dpath_along_spiral(poly, h=200, r=50, twist=1000, $fn=36);
module extrude_2dpath_along_spiral(polyline, h, r, twist=360) {
pline_count = len(polyline);
steps = ceil(segs(r)*(twist/360));
poly_points = [
for (
p = [0:steps]
) let (
a = twist * (p/steps),
dx = r*cos(a),
dy = r*sin(a),
dz = h * (p/steps),
cp = [dx, dy, dz],
rotx = matrix3_xrot(90),
rotz = matrix3_zrot(a),
rotm = rotz * rotx
) for (
b = [0:pline_count-1]
) rotm*point3d(polyline[b])+cp
// Function: path2d_regular_ngon()
// Description:
// Returns a 2D open counter-clockwise path of the vertices of a regular polygon of `n` sides.
// Usage:
// path2d_regular_ngon(n, r|d, [cp], [scale]);
// Arguments:
// n = Number of polygon sides.
// r = Radius of regular polygon.
// d = Radius of regular polygon.
// cp = Centerpoint of regular polygon. Default: `[0,0]`
// scale = [X,Y] scaling factors for each axis. Default: `[1,1]`
// Example(2D):
// trace_polyline(path2d_regular_ngon(n=12, r=50), N=1, showpts=true);
function path2d_regular_ngon(n=6, r=undef, d=undef, cp=[0,0], scale=[1,1]) = let(
rr=get_radius(r=r, d=d, dflt=100)
) [
for (i=[0:n-1])
rr * [cos(i*360/n)*scale.x, sin(i*360/n)*scale.y] + cp
];
poly_faces = concat(
[[for (b = [0:pline_count-1]) b]],
[
for (
p = [0:steps-1],
b = [0:pline_count-1],
i = [0:1]
) let (
b2 = (b == pline_count-1)? 0 : b+1,
p0 = p * pline_count + b,
p1 = p * pline_count + b2,
p2 = (p+1) * pline_count + b2,
p3 = (p+1) * pline_count + b,
pt = (i==0)? [p0, p2, p1] : [p0, p3, p2]
) pt
],
[[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]]
);
tri_faces = triangulate_faces(poly_points, poly_faces);
polyhedron(points=poly_points, faces=tri_faces, convexity=10);
}
// Function: path3d_spiral()
// Description:
// Returns a 3D spiral path.
// Usage:
// path3d_spiral(turns, h, n, r|d, [cp], [scale]);
// Arguments:
// h = Height of spiral.
// turns = Number of turns in spiral.
// n = Number of spiral sides.
// r = Radius of spiral.
// d = Radius of spiral.
// cp = Centerpoint of spiral. Default: `[0,0]`
// scale = [X,Y] scaling factors for each axis. Default: `[1,1]`
// Example(3D):
// trace_polyline(path3d_spiral(turns=2.5, h=100, n=24, r=50), N=1, showpts=true);
function path3d_spiral(turns=3, h=100, n=12, r=undef, d=undef, cp=[0,0], scale=[1,1]) = let(
rr=get_radius(r=r, d=d, dflt=100),
cnt=floor(turns*n),
dz=h/cnt
) [
for (i=[0:cnt]) [
rr * cos(i*360/n) * scale.x + cp.x,
rr * sin(i*360/n) * scale.y + cp.y,
i*dz
]
];
// Function: points_along_path3d()
// Usage:
// points_along_path3d(polyline, path);
// Description:
// Calculates the vertices needed to create a `polyhedron()` of the
// extrusion of `polyline` along `path`. The closed 2D path shold be
// centered on the XY plane. The 2D path is extruded perpendicularly
// along the 3D path. Produces a list of 3D vertices. Vertex count
// is `len(polyline)*len(path)`. Gives all the reoriented vertices
// for `polyline` at the first point in `path`, then for the second,
// and so on.
// Arguments:
// polyline = A closed list of 2D path points.
// path = A list of 3D path points.
function points_along_path3d(
polyline, // The 2D polyline to drag along the 3D path.
path, // The 3D polyline path to follow.
@ -209,19 +177,165 @@ function points_along_path3d(
);
// Takes a closed 2D polyline path, centered on the XY plane, and
// extrudes it perpendicularly along a 3D polyline path, forming a solid.
// Section: 2D Modules
// Module: modulated_circle()
// Description:
// Creates a 2D polygon circle, modulated by one or more superimposed sine waves.
// Arguments:
// r = radius of the base circle.
// sines = array of [amplitude, frequency] pairs, where the frequency is the number of times the cycle repeats around the circle.
// Example(2D):
// modulated_circle(r=40, sines=[[3, 11], [1, 31]], $fn=6);
module modulated_circle(r=40, sines=[10])
{
freqs = len(sines)>0? [for (i=sines) i[1]] : [5];
points = [
for (a = [0 : (360/segs(r)/max(freqs)) : 360])
let(nr=r+sum_of_sines(a,sines)) [nr*cos(a), nr*sin(a)]
];
polygon(points);
}
// Section: 3D Modules
// Module: extrude_from_to()
// Description:
// Extrudes a 2D shape between the points pt1 and pt2. Takes as children a set of 2D shapes to extrude.
// Arguments:
// pt1 = starting point of extrusion.
// pt2 = ending point of extrusion.
// convexity = max number of times a line could intersect a wall of the 2D shape being extruded.
// twist = number of degrees to twist the 2D shape over the entire extrusion length.
// scale = scale multiplier for end of extrusion compared the start.
// slices = Number of slices along the extrusion to break the extrusion into. Useful for refining `twist` extrusions.
// Example(FlatSpin):
// extrude_from_to([0,0,0], [10,20,30], convexity=4, twist=360, scale=3.0, slices=40) {
// xspread(3) circle(3, $fn=32);
// }
module extrude_from_to(pt1, pt2, convexity=undef, twist=undef, scale=undef, slices=undef) {
rtp = xyz_to_spherical(pt2-pt1);
translate(pt1) {
rotate([0, rtp[2], rtp[1]]) {
linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) {
children();
}
}
}
}
// Module: extrude_2d_hollow()
// Description:
// Similar to linear_extrude(), except the result is a hollow shell.
// Arguments:
// wall = thickness of shell wall.
// height = height of extrusion.
// twist = degrees of twist, from bottom to top.
// slices = how many slices to use when making extrusion.
// Example:
// extrude_2d_hollow(wall=2, height=100, twist=90, slices=50)
// circle(r=40, $fn=6);
module extrude_2d_hollow(wall=2, height=50, twist=90, slices=60, center=undef, orient=ORIENT_Z, align=V_UP)
{
orient_and_align([0,0,height], orient, align, center) {
linear_extrude(height=height, twist=twist, slices=slices) {
difference() {
children();
offset(r=-wall) {
children();
}
}
}
}
}
// Module: extrude_2dpath_along_spiral()
// Description:
// Takes a closed 2D polyline path, centered on the XY plane, and
// extrudes it along a 3D spiral path of a given radius, height and twist.
// Arguments:
// polyline = Array of points of a polyline path, to be extruded.
// h = height of the spiral to extrude along.
// r = radius of the spiral to extrude along.
// twist = number of degrees of rotation to spiral up along height.
// Example:
// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// extrude_2dpath_along_spiral(poly, h=200, r=50, twist=1080, $fn=36);
module extrude_2dpath_along_spiral(polyline, h, r, twist=360, center=undef, orient=ORIENT_Z, align=V_CENTER) {
pline_count = len(polyline);
steps = ceil(segs(r)*(twist/360));
poly_points = [
for (
p = [0:steps]
) let (
a = twist * (p/steps),
dx = r*cos(a),
dy = r*sin(a),
dz = h * (p/steps),
pts = matrix4_apply(
polyline, [
matrix4_xrot(90),
matrix4_zrot(a),
matrix4_translate([dx, dy, dz])
]
)
) for (pt = pts) pt
];
poly_faces = concat(
[[for (b = [0:pline_count-1]) b]],
[
for (
p = [0:steps-1],
b = [0:pline_count-1],
i = [0:1]
) let (
b2 = (b == pline_count-1)? 0 : b+1,
p0 = p * pline_count + b,
p1 = p * pline_count + b2,
p2 = (p+1) * pline_count + b2,
p3 = (p+1) * pline_count + b,
pt = (i==0)? [p0, p2, p1] : [p0, p3, p2]
) pt
],
[[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]]
);
tri_faces = triangulate_faces(poly_points, poly_faces);
orient_and_align([r,r,h], orient, align, center) {
polyhedron(points=poly_points, faces=tri_faces, convexity=10);
}
}
// Module: extrude_2dpath_along_3dpath()
// Description:
// Takes a closed 2D path `polyline`, centered on the XY plane, and extrudes it perpendicularly along a 3D path `path`, forming a solid.
// Arguments:
// polyline = Array of points of a polyline path, to be extruded.
// path = Array of points of a polyline path, to extrude along.
// ang = Angle in degrees to rotate 2D polyline before extrusion.
// convexity = max number of surfaces any single ray could pass through.
// Example:
// shape = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// path = [ [0, 0, 0], [100, 33, 33], [200, -33, -33], [300, 0, 0] ];
// extrude_2dpath_along_3dpath(shape, path);
module extrude_2dpath_along_3dpath(polyline, path, convexity=10) {
// Example(FlatSpin):
// shape = [[0,-10], [5,-3], [5,3], [0,10], [30,0]];
// path = concat(
// [for (a=[30:30:180]) [50*cos(a)+50, 50*sin(a), 20*sin(a)]],
// [for (a=[330:-30:180]) [50*cos(a)-50, 50*sin(a), 20*sin(a)]]
// );
// extrude_2dpath_along_3dpath(shape, path, ang=140);
module extrude_2dpath_along_3dpath(polyline, path, ang=0, convexity=10) {
pline_count = len(polyline);
path_count = len(path);
polyline = rotate_points2d(path2d(polyline), ang);
poly_points = points_along_path3d(polyline, path);
poly_faces = concat(
@ -249,13 +363,16 @@ module extrude_2dpath_along_3dpath(polyline, path, convexity=10) {
// Extrudes 2D children along a 3D polyline path.
// Module: extrude_2d_shapes_along_3dpath()
// Description:
// Extrudes 2D children along a 3D polyline path. This may be slow.
// Arguments:
// path = array of points for the bezier path to extrude along.
// convexity = maximum number of walls a ran can pass through.
// clipsize = increase if artifacts are left. Default: 1000
// Example:
// path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0] ];
// extrude_2d_shapes_along_3dpath(path) circle(r=10, center=true, $fn=5);
// Example(FlatSpin):
// path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ];
// extrude_2d_shapes_along_3dpath(path) circle(r=10, $fn=6);
module extrude_2d_shapes_along_3dpath(path, convexity=10, clipsize=100) {
function polyquats(path, q=Q_Ident(), v=[0,0,1], i=0) = let(
v2 = path[i+1] - path[i],
@ -298,5 +415,102 @@ module extrude_2d_shapes_along_3dpath(path, convexity=10, clipsize=100) {
}
// Module: trace_polyline()
// Description:
// Renders lines between each point of a polyline path.
// Can also optionally show the individual vertex points.
// Arguments:
// pline = The array of points in the polyline.
// showpts = If true, draw vertices and control points.
// N = Mark the first and every Nth vertex after in a different color and shape.
// size = Diameter of the lines drawn.
// color = Color to draw the lines (but not vertices) in.
// Example(FlatSpin):
// polyline = [for (a=[0:30:210]) 10*[cos(a), sin(a), sin(a)]];
// trace_polyline(polyline, showpts=true, size=0.5, color="lightgreen");
module trace_polyline(pline, N=1, showpts=false, size=1, color="yellow") {
if (showpts) {
for (i = [0:len(pline)-1]) {
translate(pline[i]) {
if (i%N == 0) {
color("blue") sphere(d=size*2.5, $fn=8);
} else {
color("red") {
cylinder(d=size/2, h=size*3, center=true, $fn=8);
xrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
yrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
}
}
}
}
}
for (i = [0:len(pline)-2]) {
if (N!=3 || (i%N) != 1) {
color(color) extrude_from_to(pline[i], pline[i+1]) circle(d=size/2);
}
}
}
// Module: debug_polygon()
// Description: A drop-in replacement for `polygon()` that renders and labels the path points.
// Arguments:
// points = The array of 2D polygon vertices.
// paths = The path connections between the vertices.
// convexity = The max number of walls a ray can pass through the given polygon paths.
// Example(2D):
// debug_polygon(
// points=concat(
// path2d_regular_ngon(r=10, n=8),
// path2d_regular_ngon(r=8, n=8)
// ),
// paths=[
// [for (i=[0:7]) i],
// [for (i=[15:-1:8]) i]
// ]
// );
module debug_polygon(points, paths=undef, convexity=2, size=1)
{
pths = (!is_def(paths))? [for (i=[0:len(points)-1]) i] : is_scalar(paths[0])? [paths] : paths;
echo(points=points);
echo(paths=paths);
linear_extrude(height=0.01, convexity=convexity, center=true) {
polygon(points=points, paths=paths, convexity=convexity);
}
for (i = [0:len(points)-1]) {
color("red") {
up(0.2) {
translate(points[i]) {
linear_extrude(height=0.1, convexity=10, center=true) {
text(text=str(i), size=size, halign="center", valign="center");
}
}
}
}
}
for (j = [0:len(paths)-1]) {
path = paths[j];
translate(points[path[0]]) {
color("cyan") up(0.1) cylinder(d=size*1.5, h=0.01, center=false, $fn=12);
}
translate(points[path[len(path)-1]]) {
color("pink") up(0.11) cylinder(d=size*1.5, h=0.01, center=false, $fn=4);
}
for (i = [0:len(path)-1]) {
midpt = (points[path[i]] + points[path[(i+1)%len(path)]])/2;
color("blue") {
up(0.2) {
translate(midpt) {
linear_extrude(height=0.1, convexity=10, center=true) {
text(text=str(chr(65+j),i), size=size/2, halign="center", valign="center");
}
}
}
}
}
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Phillips driver bits
// LibFile: phillips_drive.scad
// Phillips driver bits
// To use, add these lines to the top of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/phillips_drive.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -33,19 +39,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use <transforms.scad>
use <shapes.scad>
include <constants.scad>
include <compat.scad>
// Creates a model of a phillips driver bit of a given named size.
// Section: Modules
// Module: phillips_drive()
// Description: Creates a model of a phillips driver bit of a given named size.
// Arguments:
// size = The size of the bit. "#1", "#2", or "#3"
// shaft = The diameter of the drive bit's shaft.
// l = The length of the drive bit.
// Example:
// xdistribute(10) {
// phillips_drive(size="#1", shaft=4, l=30);
// phillips_drive(size="#2", shaft=6, l=30);
// phillips_drive(size="#3", shaft=6, l=30);
// phillips_drive(size="#1", shaft=4, l=20);
// phillips_drive(size="#2", shaft=6, l=20);
// phillips_drive(size="#3", shaft=6, l=20);
// }
module phillips_drive(size="#2", shaft=6, l=20) {
module phillips_drive(size="#2", shaft=6, l=20, orient=ORIENT_Z, align=V_UP) {
// These are my best guess reverse-engineered measurements of
// the tip diameters of various phillips screwdriver sizes.
ang = 11;
@ -54,31 +67,35 @@ module phillips_drive(size="#2", shaft=6, l=20) {
r = radidx == []? 0 : rads[radidx][1];
h = (r/2)/tan(ang);
cr = r/2;
difference() {
intersection() {
union() {
clip = (shaft-1.2*r)/2/tan(26.5);
zrot(360/8/2) cylinder(h=clip, d1=1.2*r/cos(360/8/2), d2=shaft/cos(360/8/2), center=false, $fn=8);
up(clip-0.01) cylinder(h=l-clip, d=shaft, center=false, $fn=24);
}
cylinder(d=shaft, h=l, center=false, $fn=24);
}
zrot(45)
zring(n=4) {
yrot(ang) {
zrot(-45) {
off = (r/2-cr*(sqrt(2)-1))/sqrt(2);
translate([off, off, 0]) {
linear_extrude(height=l*2, convexity=4) {
difference() {
union() {
square([shaft, shaft], center=false);
back(cr) zrot(1.125) square([shaft, shaft], center=false);
right(cr) zrot(-1.125) square([shaft, shaft], center=false);
}
difference() {
square([cr*2, cr*2], center=true);
translate([cr,cr,0]) circle(r=cr, $fn=8);
orient_and_align([shaft, shaft, l], orient, align) {
down(l/2) {
difference() {
intersection() {
union() {
clip = (shaft-1.2*r)/2/tan(26.5);
zrot(360/8/2) cylinder(h=clip, d1=1.2*r/cos(360/8/2), d2=shaft/cos(360/8/2), center=false, $fn=8);
up(clip-0.01) cylinder(h=l-clip, d=shaft, center=false, $fn=24);
}
cylinder(d=shaft, h=l, center=false, $fn=24);
}
zrot(45)
zring(n=4) {
yrot(ang) {
zrot(-45) {
off = (r/2-cr*(sqrt(2)-1))/sqrt(2);
translate([off, off, 0]) {
linear_extrude(height=l*2, convexity=4) {
difference() {
union() {
square([shaft, shaft], center=false);
back(cr) zrot(1.125) square([shaft, shaft], center=false);
right(cr) zrot(-1.125) square([shaft, shaft], center=false);
}
difference() {
square([cr*2, cr*2], center=true);
translate([cr,cr,0]) circle(r=cr, $fn=8);
}
}
}
}
}

View file

@ -1,5 +1,10 @@
///////////////////////////////////////////
// Quaternions
// LibFile: quaternions.scad
// Support for Quaternions.
// To use, add the following line to the beginning of your file:
// ```
// use <BOSL/quaternions.scad>
// ```
///////////////////////////////////////////
/*
@ -34,17 +39,63 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use <math.scad>
// Quaternions are stored internally as a 4-value vector:
// [X, Y, Z, W] = W + Xi + Yj + Zk
// Section: Quaternions
// Quaternions are fast methods of storing and calculating arbitrary rotations.
// Quaternions contain information on both axis of rotation, and rotation angle.
// You can chain multiple rotation together by multiplying quaternions together.
// They don't suffer from the gimbal-lock issues that [X,Y,Z] rotation angles do.
// Quaternions are stored internally as a 4-value vector:
// `[X, Y, Z, W] = W + Xi + Yj + Zk`
// Internal
function _Quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w];
// Function: Quat()
// Usage:
// Quat(ax, ang);
// Description: Create a new Quaternion from axis and angle of rotation.
// Arguments:
// ax = Vector of axis of rotation.
// ang = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
function Quat(ax=[0,0,1], ang=0) = _Quat(ax/norm(ax), sin(ang/2), cos(ang/2));
// Function: QuatX()
// Usage:
// QuatX(a);
// Description: Create a new Quaternion for rotating around the X axis [1,0,0].
// Arguments:
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
function QuatX(a=0) = Quat([1,0,0],a);
// Function: QuatY()
// Usage:
// QuatY(a);
// Description: Create a new Quaternion for rotating around the Y axis [0,1,0].
// Arguments:
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
function QuatY(a=0) = Quat([0,1,0],a);
// Function: QuatZ()
// Usage:
// QuatZ(a);
// Description: Create a new Quaternion for rotating around the Z axis [0,0,1].
// Arguments:
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
function QuatZ(a=0) = Quat([0,0,1],a);
// Creates a quaternion from standard [X,Y,Z] euller rotation angles in degrees.
function QuatEuller(a=[0,0,0]) =
// Function: QuatXYZ()
// Usage:
// QuatXYZ([X,Y,Z])
// Description:
// Creates a quaternion from standard [X,Y,Z] rotation angles in degrees.
// Arguments:
// a = The triplet of rotation angles, [X,Y,Z]
function QuatXYZ(a=[0,0,0]) =
let(
qx = QuatX(a[0]),
qy = QuatY(a[1]),
@ -52,42 +103,138 @@ function QuatEuller(a=[0,0,0]) =
)
Q_Mul(qz, Q_Mul(qy, qx));
// Function: Q_Ident()
// Description: Returns the "Identity" zero-rotation Quaternion.
function Q_Ident() = [0, 0, 0, 1];
function Q_Add_S(q, s) = [q[0], q[1], q[2], q[3]+s];
function Q_Sub_S(q, s) = [q[0], q[1], q[2], q[3]-s];
function Q_Mul_S(q, s) = [q[0]*s, q[1]*s, q[2]*s, q[3]*s];
function Q_Div_S(q, s) = [q[0]/s, q[1]/s, q[2]/s, q[3]/s];
function Q_Add(a, b) = [a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3]];
function Q_Sub(a, b) = [a[0]-b[0], a[1]-b[1], a[2]-b[2], a[3]-b[3]];
// Function: Q_Add_S()
// Usage:
// Q_Add_S(q, s)
// Description: Adds a scalar value `s` to the W part of a quaternion `q`.
function Q_Add_S(q, s) = q+[0,0,0,s];
// Function: Q_Sub_S()
// Usage:
// Q_Sub_S(q, s)
// Description: Subtracts a scalar value `s` from the W part of a quaternion `q`.
function Q_Sub_S(q, s) = q-[0,0,0,s];
// Function: Q_Mul_S()
// Usage:
// Q_Mul_S(q, s)
// Description: Multiplies each part of a quaternion `q` by a scalar value `s`.
function Q_Mul_S(q, s) = q*s;
// Function: Q_Div_S()
// Usage:
// Q_Div_S(q, s)
// Description: Divides each part of a quaternion `q` by a scalar value `s`.
function Q_Div_S(q, s) = q/s;
// Function: Q_Add()
// Usage:
// Q_Add(a, b)
// Description: Adds each part of two quaternions together.
function Q_Add(a, b) = a+b;
// Function: Q_Sub()
// Usage:
// Q_Sub(a, b)
// Description: Subtracts each part of quaternion `b` from quaternion `a`.
function Q_Sub(a, b) = a-b;
// Function: Q_Mul()
// Usage:
// Q_Mul(a, b)
// Description: Multiplies quaternion `a` by quaternion `b`.
function Q_Mul(a, b) = [
a[3]*b[0] + a[0]*b[3] + a[1]*b[2] - a[2]*b[1],
a[3]*b[1] - a[0]*b[2] + a[1]*b[3] + a[2]*b[0],
a[3]*b[2] + a[0]*b[1] - a[1]*b[0] + a[2]*b[3],
a[3]*b[3] - a[0]*b[0] - a[1]*b[1] - a[2]*b[2],
a[3]*b.x + a.x*b[3] + a.y*b.z - a.z*b.y,
a[3]*b.y - a.x*b.z + a.y*b[3] + a.z*b.x,
a[3]*b.z + a.x*b.y - a.y*b.x + a.z*b[3],
a[3]*b[3] - a.x*b.x - a.y*b.y - a.z*b.z,
];
// Function: Q_Dot()
// Usage:
// Q_Dot(a, b)
// Description: Calculates the dot product between quaternions `a` and `b`.
function Q_Dot(a, b) = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
function Q_Neg(q) = [-q[0], -q[1], -q[2], -q[3]];
function Q_Conj(q) = [-q[0], -q[1], -q[2], q[3]];
function Q_Norm(q) = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
function Q_Normalize(q) = q/Q_Norm(q);
function Q_Dist(q1, q2) = Q_Norm(Q_Sub(q1-q2));
// Function: Q_Neg()
// Usage:
// Q_Neg(q)
// Description: Returns the negative of quaternion `q`.
function Q_Neg(q) = -q;
// Returns a spherical interpolation between two quaternions.
function Q_Slerp(q1, q2, t) = let(
// Function: Q_Conj()
// Usage:
// Q_Conj(q)
// Description: Returns the conjugate of quaternion `q`.
function Q_Conj(q) = [-q.x, -q.y, -q.z, q[3]];
// Function: Q_Norm()
// Usage:
// Q_Norm(q)
// Description: Returns the `norm()` "length" of quaternion `q`.
function Q_Norm(q) = norm(q);
// Function: Q_Normalize()
// Usage:
// Q_Normalize(q)
// Description: Normalizes quaternion `q`, so that norm([W,X,Y,Z]) == 1.
function Q_Normalize(q) = q/norm(q);
// Function: Q_Dist()
// Usage:
// Q_Dist(q1, q2)
// Description: Returns the "distance" between two quaternions.
function Q_Dist(q1, q2) = norm(q2-q1);
// Function: Q_Slerp()
// Usage:
// Q_Slerp(q1, q2, u);
// Description:
// Returns a quaternion that is a spherical interpolation between two quaternions.
// Arguments:
// q1 = The first quaternion. (u=0)
// q2 = The second quaternion. (u=1)
// u = The proportional value, from 0 to 1, of what part of the interpolation to return.
// Example(3D):
// a = QuatY(15);
// b = QuatY(75);
// color("blue",0.25) Qrot(a) cylinder(d=1, h=80);
// color("red",0.25) Qrot(b) cylinder(d=1, h=80);
// Qrot(Q_Slerp(a, b, 0.6)) cylinder(d=1, h=80);
function Q_Slerp(q1, q2, u) = let(
dot = Q_Dot(q1, q2),
qq2 = dot<0? Q_Neg(q2) : q2,
dott = dot<0? -dot : dot,
theta = t * acos(constrain(dott,-1,1))
theta = u * acos(constrain(dott,-1,1))
) (dott>0.9995)?
Q_Normalize(Q_Add(q1, Q_Mul_S(Q_Sub(qq2,q1), t))) :
Q_Add(Q_Mul_S(q1,cos(theta)), Q_Mul_S(Q_Normalize(Q_Sub(qq2, Q_Mul_S(q1, dott))), sin(theta)));
Q_Normalize(q1 + ((qq2-q1) * u)) :
(q1*cos(theta) + (Q_Normalize(qq2 - (q1 * dott)) * sin(theta)));
// Returns the 3x3 rotation matrix for the given normalized quaternion q.
// Function: Q_Matrix3()
// Usage:
// Q_Matrix3(q);
// Description:
// Returns the 3x3 rotation matrix for the given normalized quaternion q.
function Q_Matrix3(q) = [
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]],
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]],
@ -95,7 +242,11 @@ function Q_Matrix3(q) = [
];
// Returns the 4x4 rotation matrix for the given normalized quaternion q.
// Function: Q_Matrix4()
// Usage:
// Q_Matrix4(q);
// Description:
// Returns the 4x4 rotation matrix for the given normalized quaternion q.
function Q_Matrix4(q) = [
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0],
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0],
@ -104,19 +255,39 @@ function Q_Matrix4(q) = [
];
// Returns the quaternion's axis of rotation as a vector.
// Function: Q_Axis()
// Usage:
// Q_Axis(q)
// Description:
// Returns the axis of rotation of a normalized quaternion `q`.
function Q_Axis(q) = let(d = sqrt(1-(q[3]*q[3]))) (d==0)? [0,0,1] : [q[0]/d, q[1]/d, q[2]/d];
// Returns the quaternion's angle of rotation in degrees.
// Function: Q_Angle()
// Usage:
// Q_Angle(q)
// Description:
// Returns the angle of rotation (in degrees) of a normalized quaternion `q`.
function Q_Angle(q) = 2 * acos(q[3]);
// Returns the vector `v` after rotating it by the quaternion `q`.
// Function: Q_Rot_Vector()
// Usage:
// Q_Rot_Vector(v,q);
// Description:
// Returns the vector `v` after rotating it by the quaternion `q`.
function Q_Rot_Vector(v,q) = Q_Mul(Q_Mul(q,concat(v,0)),Q_Conj(q));
// Rotates all children by the given quaternion q.
// Module: Qrot()
// Usage:
// Qrot(q) ...
// Description:
// Rotate all children by the rotation stored in quaternion `q`.
// Example(FlatSpin):
// q = QuatXYZ([45,35,10]);
// color("red",0.25) cylinder(d=1,h=80);
// Qrot(q) cylinder(d=1,h=80);
module Qrot(q) {
multmatrix(Q_Matrix4(q)) {
children();

653
scripts/docs_gen.py Executable file
View file

@ -0,0 +1,653 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import re
import sys
import math
import random
import os.path
import argparse
import subprocess
def get_header_link(name):
refpat = re.compile("[^a-z0-9_ -]")
return refpat.sub("", name.lower()).replace(" ", "-")
def toc_entry(name, indent, count=None):
ref = get_header_link(name)
if name.endswith( (")", "}", "]") ):
name = "`" + name.replace("\\", "") + "`"
return "{0}{1} [{2}](#{3})".format(
indent,
("%d." % count) if count else "-",
name,
ref
)
def mkdn_esc(txt):
out = ""
quotpat = re.compile(r'([^`]*)(`[^`]*`)(.*$)');
while txt:
m = quotpat.match(txt)
if m:
out += m.group(1).replace(r'_', r'\_')
out += m.group(2)
txt = m.group(3)
else:
out += txt.replace(r'_', r'\_')
txt = ""
return out
def get_comment_block(lines, prefix, blanks=1):
out = []
blankcnt = 0
while lines:
if not lines[0].startswith(prefix + " "):
break
line = lines.pop(0).rstrip().lstrip("/")
if line == "":
blankcnt += 1
if blankcnt >= blanks:
break
else:
blankcnt = 0
line = line[len(prefix):]
out.append(line)
return (lines, out)
class ImageProcessing(object):
def __init__(self):
self.examples = []
self.commoncode = []
self.imgroot = ""
self.keep_scripts = False
def set_keep_scripts(self, x):
self.keep_scripts = x
def add_image(self, libfile, imgfile, code, extype):
self.examples.append((libfile, imgfile, code, extype))
def set_commoncode(self, code):
self.commoncode = code
def process_examples(self, imgroot):
self.imgroot = imgroot
for libfile, imgfile, code, extype in self.examples:
self.gen_example_image(libfile, imgfile, code, extype)
def gen_example_image(self, libfile, imgfile, code, extype):
OPENSCAD = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
CONVERT = "/usr/local/bin/convert"
if extype == "NORENDER":
return
scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_"))
stdlibs = ["constants.scad", "math.scad", "transforms.scad", "shapes.scad", "debug.scad"]
script = ""
for lib in stdlibs:
script += "include <BOSL/%s>\n" % lib
if libfile not in stdlibs:
script += "include <BOSL/%s>\n" % libfile
for line in self.commoncode:
script += line+"\n"
for line in code:
script += line+"\n"
with open(scriptfile, "w") as f:
f.write(script)
if "Med" in extype:
imgsizes = ["800,600", "400x300"]
elif "Big" in extype:
imgsizes = ["1280,960", "640x480"]
elif "distribute" in script:
print(script)
imgsizes = ["800,600", "400x300"]
else: # Small
imgsizes = ["480,360", "240x180"]
print("")
print("{}: {}".format(libfile, imgfile))
tmpimgs = []
if "Spin" in extype:
for ang in range(0,359,10):
tmpimgfile = "{0}tmp_{2}_{1}.png".format(self.imgroot, ang, imgfile.replace(".", "_"))
arad = ang * math.pi / 180;
eye = "{0},{1},{2}".format(
500*math.cos(arad),
500*math.sin(arad),
500 if "Flat" in extype else 500*math.sin(arad)
)
scadcmd = [
OPENSCAD,
"-o", tmpimgfile,
"--imgsize={}".format(imgsizes[0]),
"--hardwarnings",
"--projection=o",
"--view=axes,scales",
"--autocenter",
"--viewall",
"--camera", eye+",0,0,0"
]
if "FR" in extype: # Force render
scadcmd.extend(["--render", ""])
scadcmd.append(scriptfile)
print(" ".join(scadcmd))
res = subprocess.call(scadcmd)
if res != 0:
print(script)
sys.exit(res)
tmpimgs.append(tmpimgfile)
else:
tmpimgfile = self.imgroot + "tmp_" + imgfile
scadcmd = [
OPENSCAD,
"-o", tmpimgfile,
"--imgsize={}".format(imgsizes[0]),
"--hardwarnings",
"--projection=o",
"--view=axes,scales",
"--autocenter",
"--viewall"
]
if "2D" in extype: # 2D viewpoint
scadcmd.extend(["--camera", "0,0,0,0,0,0,500"])
if "FR" in extype: # Force render
scadcmd.extend(["--render", ""])
scadcmd.append(scriptfile)
print(" ".join(scadcmd))
res = subprocess.call(scadcmd)
if res != 0:
print(script)
sys.exit(res)
tmpimgs.append(tmpimgfile)
if not self.keep_scripts:
os.unlink(scriptfile)
outimgfile = self.imgroot + imgfile
if len(tmpimgs) == 1:
cnvcmd = [CONVERT, tmpimgfile, "-resize", imgsizes[1], outimgfile]
print(" ".join(cnvcmd))
res = subprocess.call(cnvcmd)
if res != 0:
sys.exit(res)
os.unlink(tmpimgs.pop(0))
else:
cnvcmd = [
CONVERT,
"-delay", "25",
"-loop", "0",
"-coalesce",
"-scale", imgsizes[1],
"-fuzz", "2%",
"+dither",
"-layers", "Optimize",
"+map"
]
cnvcmd.extend(tmpimgs)
cnvcmd.append(outimgfile)
print(" ".join(cnvcmd))
res = subprocess.call(cnvcmd)
if res != 0:
sys.exit(res)
for tmpimg in tmpimgs:
os.unlink(tmpimg)
imgprc = ImageProcessing()
class LeafNode(object):
def __init__(self):
self.name = ""
self.leaftype = ""
self.status = ""
self.description = []
self.usages = []
self.arguments = []
self.side_effects = []
self.examples = []
@classmethod
def match_line(cls, line, prefix):
if line.startswith(prefix + "Constant: "):
return True
if line.startswith(prefix + "Function: "):
return True
if line.startswith(prefix + "Module: "):
return True
return False
def add_example(self, title, code, extype):
self.examples.append((title, code, extype))
def parse_lines(self, lines, prefix):
blankcnt = 0
expat = re.compile(r"^(Examples?)(\(([^\)]*)\))?: *(.*)$")
while lines:
if prefix and not lines[0].startswith(prefix.strip()):
break
line = lines.pop(0).rstrip()
if line.lstrip("/").strip() == "":
blankcnt += 1
if blankcnt >= 2:
break
continue
blankcnt = 0
line = line[len(prefix):]
if line.startswith("Constant:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
if line.startswith("Function:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
if line.startswith("Module:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
if line.startswith("Status:"):
dummy, status = line.split(":", 1)
self.status = status.strip()
if line.startswith("Description:"):
dummy, desc = line.split(":", 1)
desc = desc.strip()
if desc:
self.description.append(desc)
lines, block = get_comment_block(lines, prefix)
self.description.extend(block)
if line.startswith("Usage:"):
dummy, title = line.split(":", 1)
title = title.strip()
lines, block = get_comment_block(lines, prefix)
self.usages.append([title, block])
if line.startswith("Arguments:"):
lines, block = get_comment_block(lines, prefix)
for line in block:
if "=" not in line:
print("Error: bad argument line:")
print(line)
sys.exit(2)
argname, argdesc = line.split("=", 1)
argname = argname.strip()
argdesc = argdesc.strip()
self.arguments.append([argname, argdesc])
if line.startswith("Side Effects:"):
lines, block = get_comment_block(lines, prefix)
self.side_effects.extend(block)
m = expat.match(line)
if m: # Example(TYPE):
plural = m.group(1) == "Examples"
extype = m.group(3)
title = m.group(4)
lines, block = get_comment_block(lines, prefix)
if not extype:
extype = "3D" if self.leaftype == "Module" else "NORENDER"
if not plural:
self.add_example(title=title, code=block, extype=extype)
else:
for line in block:
self.add_example(title="", code=[line], extype=extype)
return lines
def gen_md(self, fileroot, imgroot):
out = []
if self.name:
out.append("### " + mkdn_esc(self.name))
out.append("")
if self.status:
out.append("**{0}**".format(mkdn_esc(self.status)))
out.append("")
for title, usages in self.usages:
if not title:
title = "Usage"
out.append("**{0}**:".format(mkdn_esc(title)))
for usage in usages:
out.append("- {0}".format(mkdn_esc(usage)))
out.append("")
if self.description:
out.append("**Description**:")
for line in self.description:
out.append(mkdn_esc(line))
out.append("")
if self.arguments:
out.append("Argument | What it does")
out.append("--------------- | ------------------------------")
for argname, argdesc in self.arguments:
argname = argname.replace(" / ", "` / `")
out.append(
"{0:15s} | {1}".format(
"`{0}`".format(argname),
mkdn_esc(argdesc)
)
)
out.append("")
if self.side_effects:
out.append("**Side Effects**:")
for sfx in self.side_effects:
out.append("- " + mkdn_esc(sfx))
out.append("")
exnum = 0
for title, excode, extype in self.examples:
exnum += 1
if len(self.examples) < 2:
extitle = "**Example**:"
else:
extitle = "**Example {0}**:".format(exnum)
if title:
extitle += " " + mkdn_esc(title)
out.append(extitle)
out.append("")
for line in excode:
out.append(" " + line)
out.append("")
san_name = re.sub(r"[^A-Za-z0-9_]", "", self.name)
imgfile = "{0}{1}.{2}".format(
san_name,
("_%d" % exnum) if exnum > 1 else "",
"gif" if "Spin" in extype else "png"
)
if extype != "NORENDER":
out.append(
"![{0} Example{1}]({2}{3})".format(
mkdn_esc(self.name),
(" %d" % exnum) if len(self.examples) > 1 else "",
imgroot,
imgfile
)
)
out.append("")
imgprc.add_image(fileroot+".scad", imgfile, excode, extype)
out.append("---")
out.append("")
return out
class Section(object):
fignum = 0
def __init__(self):
self.name = ""
self.description = []
self.leaf_nodes = []
self.figures = []
@classmethod
def match_line(cls, line, prefix):
if line.startswith(prefix + "Section: "):
return True
return False
def add_figure(self, figtitle, figcode, figtype):
self.figures.append((figtitle, figcode, figtype))
def parse_lines(self, lines, prefix):
line = lines.pop(0).rstrip()
dummy, title = line.split(": ", 1)
self.name = title.strip()
lines, block = get_comment_block(lines, prefix, blanks=2)
self.description.extend(block)
blankcnt = 0
figpat = re.compile(r"^(Figures?)(\(([^\)]*)\))?: *(.*)$")
while lines:
if prefix and not lines[0].startswith(prefix.strip()):
break
line = lines.pop(0).rstrip()
if line.lstrip("/").strip() == "":
blankcnt += 1
if blankcnt >= 2:
break
continue
blankcnt = 0
line = line[len(prefix):]
m = figpat.match(line)
if m: # Figures(TYPE):
plural = m.group(1) == "Figures"
figtype = m.group(3)
title = m.group(4)
lines, block = get_comment_block(lines, prefix)
if not figtype:
figtype = "3D" if self.figtype == "Module" else "NORENDER"
if not plural:
self.add_figure(title, block, figtype)
else:
for line in block:
self.add_figure("", [line], figtype)
return lines
def gen_md_toc(self, count):
indent=""
out = []
if self.name:
out.append(toc_entry(self.name, indent, count=count))
indent += " "
for node in self.leaf_nodes:
out.append(toc_entry(node.name, indent))
out.append("")
return out
def gen_md(self, count, fileroot, imgroot):
out = []
if self.name:
out.append("# %d. %s" % (count, mkdn_esc(self.name)))
out.append("")
if self.description:
in_block = False
for line in self.description:
if line.startswith("```"):
in_block = not in_block
if in_block or line.startswith(" "):
out.append(line)
else:
out.append(mkdn_esc(line))
out.append("")
for title, figcode, figtype in self.figures:
Section.fignum += 1
figtitle = "**Figure {0}**:".format(Section.fignum)
if title:
figtitle += " " + mkdn_esc(title)
out.append(figtitle)
out.append("")
imgfile = "{}{}.{}".format(
"figure",
Section.fignum,
"gif" if "Spin" in figtype else "png"
)
if figtype != "NORENDER":
out.append(
"![{0} Figure {1}]({2}{3})".format(
mkdn_esc(self.name),
Section.fignum,
imgroot,
imgfile
)
)
out.append("")
imgprc.add_image(fileroot+".scad", imgfile, figcode, figtype)
in_block = False
for node in self.leaf_nodes:
out += node.gen_md(fileroot, imgroot)
return out
class LibFile(object):
def __init__(self):
self.name = ""
self.description = []
self.commoncode = []
self.sections = []
self.dep_sect = None
def parse_lines(self, lines, prefix):
currsect = None
constpat = re.compile(r"^([A-Z_0-9][A-Z_0-9]*) *=.* // (.*$)")
while lines:
while lines and prefix and not lines[0].startswith(prefix.strip()):
line = lines.pop(0)
m = constpat.match(line)
if m:
if currsect == None:
currsect = Section()
self.sections.append(currsect)
node = LeafNode();
node.extype = "Constant"
node.name = m.group(1).strip()
node.description.append(m.group(2).strip())
currsect.leaf_nodes.append(node)
# Check for LibFile header.
if lines and lines[0].startswith(prefix + "LibFile: "):
line = lines.pop(0).rstrip()
dummy, title = line.split(": ", 1)
self.name = title.strip()
lines, block = get_comment_block(lines, prefix, blanks=2)
self.description.extend(block)
# Check for CommonCode header.
if lines and lines[0].startswith(prefix + "CommonCode:"):
lines.pop(0)
lines, block = get_comment_block(lines, prefix)
self.commoncode.extend(block)
# Check for Section header.
if lines and Section.match_line(lines[0], prefix):
sect = Section()
lines = sect.parse_lines(lines, prefix)
self.sections.append(sect)
currsect = sect
# Check for LeafNode.
if lines and LeafNode.match_line(lines[0], prefix):
node = LeafNode()
lines = node.parse_lines(lines, prefix)
deprecated = node.status.startswith("DEPRECATED")
if deprecated:
if self.dep_sect == None:
self.dep_sect = Section()
self.dep_sect.name = "Deprecations"
sect = self.dep_sect
else:
if currsect == None:
currsect = Section()
self.sections.append(currsect)
sect = currsect
sect.leaf_nodes.append(node)
if lines:
lines.pop(0)
return lines
def gen_md(self, fileroot, imgroot):
imgprc.set_commoncode(self.commoncode)
out = []
if self.name:
out.append("# Library File " + mkdn_esc(self.name))
out.append("")
if self.description:
in_block = False
for line in self.description:
if line.startswith("```"):
in_block = not in_block
if in_block or line.startswith(" "):
out.append(line)
else:
out.append(mkdn_esc(line))
out.append("")
in_block = False
if self.name or self.description:
out.append("---")
out.append("")
if self.sections or self.dep_sect:
out.append("# Table of Contents")
out.append("")
cnt = 0
for sect in self.sections:
cnt += 1
out += sect.gen_md_toc(cnt)
if self.dep_sect:
cnt += 1
out += self.dep_sect.gen_md_toc(cnt)
out.append("---")
out.append("")
cnt = 0
for sect in self.sections:
cnt += 1
out += sect.gen_md(cnt, fileroot, imgroot)
if self.dep_sect:
cnt += 1
out += self.dep_sect.gen_md(cnt, fileroot, imgroot)
return out
def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix=""):
if imgroot and not imgroot.endswith('/'):
imgroot += "/"
libfile = LibFile()
with open(infile, "r") as f:
lines = f.readlines()
libfile.parse_lines(lines, prefix)
if outfile == None:
f = sys.stdout
else:
f = open(outfile, "w")
fileroot = os.path.splitext(os.path.basename(infile))[0]
outdata = libfile.gen_md(fileroot, imgroot)
for line in outdata:
print(line, file=f)
if gen_imgs:
imgprc.process_examples(imgroot)
if outfile:
f.close()
def main():
parser = argparse.ArgumentParser(prog='docs_gen')
parser.add_argument('-k', '--keep-scripts', action="store_true",
help="If given, don't delete the temporary image OpenSCAD scripts.")
parser.add_argument('-c', '--comments-only', action="store_true",
help='If given, only process lines that start with // comments.')
parser.add_argument('-i', '--images', action="store_true",
help='If given, generate images for examples with OpenSCAD.')
parser.add_argument('-I', '--imgroot', default="",
help='The directory to put generated images in.')
parser.add_argument('-o', '--outfile',
help='Output file, if different from infile.')
parser.add_argument('infile', help='Input filename.')
args = parser.parse_args()
imgprc.set_keep_scripts(args.keep_scripts)
processFile(
args.infile,
outfile=args.outfile,
gen_imgs=args.images,
imgroot=args.imgroot,
prefix="// " if args.comments_only else ""
)
sys.exit(0)
if __name__ == "__main__":
main()
# vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

22
scripts/make_all_docs.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash
PREVIEW_LIBS="constants compat transforms shapes masks paths beziers math metric_screws threading involute_gears sliders joiners linear_bearings nema_steppers wiring triangulation quaternions phillips_drive torx_drive debug"
dir="$(basename $PWD)"
if [ "$dir" = "BOSL" ]; then
cd BOSL.wiki
elif [ "$dir" != "BOSL.wiki" ]; then
echo "Must run this script from the BOSL or BOSL/BOSL.wiki directories."
exit 1
fi
rm -f tmpscad*.scad
for lib in $PREVIEW_LIBS; do
mkdir -p images/$lib
rm -f images/$lib/*.png images/$lib/*.gif
echo ../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c -i -I images/$lib/
../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c -i -I images/$lib/ || exit 1
open -a Typora $lib.scad.md
done

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Sliders and Rails.
// LibFile: sliders.scad
// Simple V-groove based sliders and rails.
// To use, add these lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/sliders.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -31,71 +37,51 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use <transforms.scad>
use <shapes.scad>
include <compat.scad>
include <constants.scad>
// Creates a slider to match a rail.
// l = length (long axis) of slider.
// w = width of slider.
// h = height of slider.
// base = height of slider base.
// wall = width of wall behind each side of the slider.
// ang = overhang angle for slider, to facilitate supportless printig.
// slop = printer-specific slop value to make parts fit exactly.
// Section: Modules
// Module: slider()
// Description:
// Creates a slider to match a V-groove rail.
// Usage:
// slider(l, w, h, [base], [wall], [ang], [slop], [orient], [align])
// Arguments:
// l = Length (long axis) of slider.
// w = Width of slider.
// h = Height of slider.
// base = Height of slider base.
// wall = Width of wall behind each side of the slider.
// ang = Overhang angle for slider, to facilitate supportless printig.
// slop = Printer-specific slop value to make parts fit exactly.
// orient = Orientation of the slider. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the slider. Use the `V_` constants from `constants.scad`. Default: `V_UP`.
// Example:
// slider(l=30, base=10, wall=4, slop=0.2);
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=printer_slop)
// slider(l=30, base=10, wall=4, slop=0.2, orient=ORIENT_Y);
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=PRINTER_SLOP, orient=ORIENT_Y, align=V_UP)
{
full_width = w + 2*wall;
full_height = h + base;
difference() {
// Overall slider shell
up(full_height/2) cube([w+2*wall, l, full_height], center=true);
orient_and_align([full_width, l, h+2*base], orient, align, orig_orient=ORIENT_Y) {
down(base+h/2) {
// Base
cuboid([full_width, l, base-slop], chamfer=2, edges=EDGE_TOP_FR+EDGE_TOP_BK+EDGES_Z_ALL, align=V_UP);
up(base-slop) {
// Clear slider gap
up((h+5)/2) {
cube([w+slop, l+1, h+5], center=true);
// Wall
xflip_copy(offset=w/2+slop) {
cuboid([wall, l, full_height], chamfer=2, edges=EDGE_TOP_RT+EDGE_FR_RT+EDGE_BK_RT, align=V_UP+V_RIGHT);
}
// Horiz edge bevel
yspread(l) {
scale([1, 1, tan(30)]) {
xrot(45) cube([w+slop, 2*sqrt(2), 2*sqrt(2)], center=true);
}
}
}
// Back top bevel
up(full_height) {
xspread(full_width) {
yrot(45) {
cube([wall/2*sqrt(2), l+1, wall/2*sqrt(2)], center=true);
}
}
}
}
up(base) {
up(h/2) {
xflip_copy() {
left((w+slop)/2) {
difference() {
// Rails
right_half() {
scale([tan(ang), 1, 1]) {
yrot(45) cube([h*sin(45), l, h*sin(45)], center=true);
}
}
// Rail bevels
yflip_copy() {
right(sqrt(2)*h/2) {
fwd(l/2) {
zrot(45) cube(h, center=true);
}
}
}
}
// Sliders
up(base+h/2) {
xflip_copy(offset=w/2+slop+0.02) {
bev_h = h/2*tan(ang);
prismoid([l, h], [l-w, 0], h=bev_h+0.01, orient=ORIENT_XNEG, align=V_LEFT);
}
}
}
@ -104,15 +90,22 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=printer_slop)
// Creates a slider to match a rail.
// l = length (long axis) of slider.
// w = width of slider.
// h = height of slider.
// chamfer = size of chamfer at end of rail.
// ang = overhang angle for slider, to facilitate supportless printig.
// Module: rail()
// Description:
// Creates a V-groove rail.
// Usage:
// rail(l, w, h, [chamfer], [ang], [orient], [align])
// Arguments:
// l = Length (long axis) of slider.
// w = Width of slider.
// h = Height of slider.
// chamfer = Size of chamfer at end of rail.
// ang = Overhang angle for slider, to facilitate supportless printig.
// orient = Orientation of the rail. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
// align = Alignment of the rail. Use the `V_` constants from `constants.scad`. Default: `V_UP`.
// Example:
// rail(l=100, w=10, h=10);
module rail(l=30, w=10, h=10, chamfer=1.0, ang=30)
module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, orient=ORIENT_Y, align=V_UP)
{
attack_ang = 30;
attack_len = 2;
@ -138,102 +131,103 @@ module rail(l=30, w=10, h=10, chamfer=1.0, ang=30)
y1 = l/2;
y2 = y1 - attack_len * cos(attack_ang);
polyhedron(
convexity=4,
points=[
[-x5, -y1, z3],
[ x5, -y1, z3],
[ x7, -y1, z4],
[ x4, -y1, -z1-0.05],
[-x4, -y1, -z1-0.05],
[-x7, -y1, z4],
orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y) {
polyhedron(
convexity=4,
points=[
[-x5, -y1, z3],
[ x5, -y1, z3],
[ x7, -y1, z4],
[ x4, -y1, -z1-0.05],
[-x4, -y1, -z1-0.05],
[-x7, -y1, z4],
[-x3, -y2, z1],
[ x3, -y2, z1],
[ x2, -y2, z2],
[ x6, -y2, z4],
[ x1, -y2, -z1-0.05],
[-x1, -y2, -z1-0.05],
[-x6, -y2, z4],
[-x2, -y2, z2],
[-x3, -y2, z1],
[ x3, -y2, z1],
[ x2, -y2, z2],
[ x6, -y2, z4],
[ x1, -y2, -z1-0.05],
[-x1, -y2, -z1-0.05],
[-x6, -y2, z4],
[-x2, -y2, z2],
[ x5, y1, z3],
[-x5, y1, z3],
[-x7, y1, z4],
[-x4, y1, -z1-0.05],
[ x4, y1, -z1-0.05],
[ x7, y1, z4],
[ x5, y1, z3],
[-x5, y1, z3],
[-x7, y1, z4],
[-x4, y1, -z1-0.05],
[ x4, y1, -z1-0.05],
[ x7, y1, z4],
[ x3, y2, z1],
[-x3, y2, z1],
[-x2, y2, z2],
[-x6, y2, z4],
[-x1, y2, -z1-0.05],
[ x1, y2, -z1-0.05],
[ x6, y2, z4],
[ x2, y2, z2],
],
faces=[
[0, 1, 2],
[0, 2, 5],
[2, 3, 4],
[2, 4, 5],
[ x3, y2, z1],
[-x3, y2, z1],
[-x2, y2, z2],
[-x6, y2, z4],
[-x1, y2, -z1-0.05],
[ x1, y2, -z1-0.05],
[ x6, y2, z4],
[ x2, y2, z2],
],
faces=[
[0, 1, 2],
[0, 2, 5],
[2, 3, 4],
[2, 4, 5],
[0, 13, 6],
[0, 6, 7],
[0, 7, 1],
[1, 7, 8],
[1, 8, 9],
[1, 9, 2],
[2, 9, 10],
[2, 10, 3],
[3, 10, 11],
[3, 11, 4],
[4, 11, 12],
[4, 12, 5],
[5, 12, 13],
[5, 13, 0],
[0, 13, 6],
[0, 6, 7],
[0, 7, 1],
[1, 7, 8],
[1, 8, 9],
[1, 9, 2],
[2, 9, 10],
[2, 10, 3],
[3, 10, 11],
[3, 11, 4],
[4, 11, 12],
[4, 12, 5],
[5, 12, 13],
[5, 13, 0],
[14, 15, 16],
[14, 16, 19],
[16, 17, 18],
[16, 18, 19],
[14, 15, 16],
[14, 16, 19],
[16, 17, 18],
[16, 18, 19],
[14, 27, 20],
[14, 20, 21],
[14, 21, 15],
[15, 21, 22],
[15, 22, 23],
[15, 23, 16],
[16, 23, 24],
[16, 24, 17],
[17, 24, 25],
[17, 25, 18],
[18, 25, 26],
[18, 26, 19],
[19, 26, 27],
[19, 27, 14],
[14, 27, 20],
[14, 20, 21],
[14, 21, 15],
[15, 21, 22],
[15, 22, 23],
[15, 23, 16],
[16, 23, 24],
[16, 24, 17],
[17, 24, 25],
[17, 25, 18],
[18, 25, 26],
[18, 26, 19],
[19, 26, 27],
[19, 27, 14],
[6, 21, 20],
[6, 20, 7],
[7, 20, 27],
[7, 27, 8],
[8, 27, 26],
[8, 26, 9],
[9, 26, 25],
[9, 25, 10],
[10, 25, 24],
[10, 24, 11],
[11, 24, 23],
[11, 23, 12],
[12, 23, 22],
[12, 22, 13],
[13, 22, 21],
[13, 21, 6],
]
);
[6, 21, 20],
[6, 20, 7],
[7, 20, 27],
[7, 27, 8],
[8, 27, 26],
[8, 26, 9],
[9, 26, 25],
[9, 25, 10],
[10, 25, 24],
[10, 24, 11],
[11, 24, 23],
[11, 23, 12],
[12, 23, 22],
[12, 22, 13],
[13, 22, 21],
[13, 21, 6],
]
);
}
}
//!rail(l=30, w=10, h=10);

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Trapezoidal-threaded (ACME) Screw Rods and Nuts
// LibFile: threading.scad
// Triangular and Trapezoidal-Threaded Screw Rods and Nuts.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/threading.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -30,20 +36,27 @@ 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 <transforms.scad>
include <math.scad>
include <constants.scad>
use <transforms.scad>
use <masks.scad>
use <math.scad>
function _trpzd_thread_pt(thread, threads, start, starts, astep, asteps, part, parts) =
astep + asteps * (thread + threads * (part + parts * start));
// Constructs a generic trapezoidal threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2.
// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2.
// For square threads, use thread_angle=0 and thread_depth=pitch/2.
// For normal screw threads, use thread_angle=30 and thread_depth=pitch*3*sqrt(3)/8.
// Section: Generic Trapezoidal Threading
// Module: trapezoidal_threaded_rod()
// Description:
// Constructs a generic trapezoidal threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2.
// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2.
// For square threads, use thread_angle=0 and thread_depth=pitch/2.
// For normal screw threads, use thread_angle=30 and thread_depth=pitch*3*sqrt(3)/8.
// Arguments:
// d = Outer diameter of threaded rod.
// l = Length of threaded rod.
// pitch = Length between threads.
@ -52,6 +65,9 @@ function _trpzd_thread_pt(thread, threads, start, starts, astep, asteps, part, p
// left_handed = If true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: true
// starts = The number of lead starts. Default = 1
// orient = Orientation of the rod. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the rod. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
// Examples:
// trapezoidal_threaded_rod(d=10, l=100, pitch=2, thread_angle=15, $fn=32);
// trapezoidal_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, thread_angle=29, $fn=32);
@ -60,8 +76,8 @@ function _trpzd_thread_pt(thread, threads, start, starts, astep, asteps, part, p
// trapezoidal_threaded_rod(d=16, l=40, pitch=2, thread_angle=30);
// trapezoidal_threaded_rod(d=10, l=40, pitch=3, thread_angle=15, left_handed=true, starts=3, $fn=36);
// trapezoidal_threaded_rod(d=25, l=100, pitch=10, thread_depth=8/3, thread_angle=50, starts=4, center=false, $fa=2, $fs=2);
// trapezoidal_threaded_rod(d=50, l=75, pitch=8, thread_angle=30, starts=3);
// trapezoidal_threaded_rod(l=25, d=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1);
// trapezoidal_threaded_rod(d=50, l=75, pitch=8, thread_angle=30, starts=3, bevel=true);
// trapezoidal_threaded_rod(l=25, d=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1, orient=ORIENT_X, align=ALIGN_POS);
module trapezoidal_threaded_rod(
d=10,
l=100,
@ -70,8 +86,10 @@ module trapezoidal_threaded_rod(
thread_depth=undef,
left_handed=false,
bevel=false,
center=true,
starts=1
starts=1,
orient=ORIENT_Z,
align=V_CENTER,
center=undef
) {
astep = 360 / quantup(segs(d/2), starts);
asteps = ceil(360/astep);
@ -204,32 +222,26 @@ module trapezoidal_threaded_rod(
) otri
]
);
up(center? 0 : l/2) {
orient_and_align([d,d,l], orient, align, center) {
difference() {
polyhedron(points=poly_points, faces=poly_faces, convexity=threads*starts*2);
zspread(1.5*l) cube([d+1, d+1, l/2], center=true);
if (bevel) {
zflip_copy() {
down(l/2+0.01) {
difference() {
up(depth/2-0.01) cube([d*2, d*2, depth+0.01], center=true);
cylinder(r1=d/2-depth, r2=d/2, h=depth+0.01, center=false);
}
}
}
}
zspread(l+4*pitch*starts) cube([d+1, d+1, 4*pitch*starts], center=true);
if (bevel) cylinder_mask(d=d, l=l+0.01, chamfer=depth);
}
}
}
// Constructs a hex nut for a threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// For metric screw threads, use thread_angle=30 and leave out thread_depth argument.
// For SAE screw threads, use thread_angle=30 and leave out thread_depth argument.
// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2.
// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2.
// For square threads, use thread_angle=0 and thread_depth=pitch/2.
// Module: trapezoidal_threaded_nut()
// Description:
// Constructs a hex nut for a threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// For metric screw threads, use thread_angle=30 and leave out thread_depth argument.
// For SAE screw threads, use thread_angle=30 and leave out thread_depth argument.
// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2.
// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2.
// For square threads, use thread_angle=0 and thread_depth=pitch/2.
// Arguments:
// od = diameter of the nut.
// id = diameter of threaded rod to screw onto.
// h = height/thickness of nut.
@ -240,8 +252,10 @@ module trapezoidal_threaded_rod(
// starts = The number of lead starts. Default = 1
// slop = printer slop calibration to allow for tight fitting of parts. default=0.2
// bevel = if true, bevel the thread ends. Default: true
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// trapezoidal_threaded_nut(od=16, id=8, h=8, pitch=2, slop=0.2);
// trapezoidal_threaded_nut(od=16, id=8, h=8, pitch=2, slop=0.2, align=V_UP);
// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, slop=0.2, left_handed=true);
// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1);
module trapezoidal_threaded_nut(
@ -254,26 +268,30 @@ module trapezoidal_threaded_nut(
left_handed=false,
starts=1,
bevel=true,
slop=0.2
slop=PRINTER_SLOP,
orient=ORIENT_Z,
align=V_CENTER
) {
depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle));
difference() {
cylinder(r=od/2/cos(30), h=h, center=true, $fn=6);
zspread(slop, n=slop>0?2:1) {
trapezoidal_threaded_rod(
d=id+2*slop,
l=h+1,
pitch=pitch,
thread_depth=depth,
thread_angle=thread_angle,
left_handed=left_handed,
starts=starts
);
}
if (bevel) {
zflip_copy() {
down(h/2+0.01) {
cylinder(r1=id/2+slop, r2=id/2+slop-depth, h=depth, center=false);
orient_and_align([od/cos(30),od,h], orient, align) {
difference() {
cylinder(d=od/cos(30), h=h, center=true, $fn=6);
zspread(slop, n=slop>0?2:1) {
trapezoidal_threaded_rod(
d=id+2*slop,
l=h+1,
pitch=pitch,
thread_depth=depth,
thread_angle=thread_angle,
left_handed=left_handed,
starts=starts
);
}
if (bevel) {
zflip_copy() {
down(h/2+0.01) {
cylinder(r1=id/2+slop, r2=id/2+slop-depth, h=depth, center=false);
}
}
}
}
@ -281,30 +299,41 @@ module trapezoidal_threaded_nut(
}
// Section: Triangular Threading
// Constructs a standard metric or UTS threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Module: threaded_rod()
// Description:
// Constructs a standard metric or UTS threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Arguments:
// d = Outer diameter of threaded rod.
// l = length of threaded rod.
// pitch = Length between threads.
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// orient = Orientation of the rod. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the rod. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// threaded_rod(d=10, l=30, pitch=1.25, left_handed=true, $fa=1, $fs=1);
module threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false) {
module threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false, orient=ORIENT_Z, align=V_CENTER) {
trapezoidal_threaded_rod(
d=d, l=l, pitch=pitch,
thread_depth=pitch*3*sqrt(3)/8,
thread_angle=30,
left_handed=left_handed,
bevel=bevel
bevel=bevel,
orient=orient,
align=align
);
}
// Constructs a hex nut for a metric or UTS threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Module: threaded_nut()
// Description:
// Constructs a hex nut for a metric or UTS threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Arguments:
// od = diameter of the nut.
// id = diameter of threaded rod to screw onto.
// h = height/thickness of nut.
@ -312,32 +341,71 @@ module threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false) {
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// slop = printer slop calibration to allow for tight fitting of parts. default=0.2
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// threaded_nut(od=16, id=8, l=8, pitch=1.25, left_handed=true, slop=0.2, $fa=1, $fs=1);
module threaded_nut(od=16, id=10, h=10, pitch=2, left_handed=false, bevel=false, slop=0.2) {
trapezoidal_threaded_nut(od=od, id=id, h=h, pitch=pitch, thread_angle=30, thread_depth=pitch*3*sqrt(3)/8, left_handed=left_handed, bevel=bevel, slop=slop);
// threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, slop=0.2, $fa=1, $fs=1);
module threaded_nut(
od=16, id=10, h=10,
pitch=2, left_handed=false,
bevel=false, slop=0.2,
orient=ORIENT_Z, align=V_CENTER
) {
trapezoidal_threaded_nut(
od=od, id=id, h=h,
pitch=pitch, thread_angle=30,
thread_depth=pitch*3*sqrt(3)/8,
left_handed=left_handed,
bevel=bevel, slop=slop,
orient=orient, align=align
);
}
// Section: Metric Trapezoidal Threading
// Constructs a metric trapezoidal threaded screw rod. This method makes much
// smoother threads than the naive linear_extrude method.
// Module: metric_trapezoidal_threaded_rod()
// Description:
// Constructs a metric trapezoidal threaded screw rod. This method makes much
// smoother threads than the naive linear_extrude method.
// Arguments:
// d = Outer diameter of threaded rod.
// l = length of threaded rod.
// pitch = Length between threads.
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// starts = The number of lead starts. Default = 1
// orient = Orientation of the rod. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the rod. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// metric_trapezoidal_threaded_rod(d=10, l=30, pitch=2, left_handed=true, $fa=1, $fs=1);
module metric_trapezoidal_threaded_rod(d=10, l=100, pitch=2, left_handed=false, starts=1, bevel=false) {
trapezoidal_threaded_rod(d=d, l=l, pitch=pitch, thread_angle=15, left_handed=left_handed, starts=starts, bevel=bevel);
module metric_trapezoidal_threaded_rod(
d=10, l=100, pitch=2,
left_handed=false,
starts=1,
bevel=false,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_rod(
d=d, l=l,
pitch=pitch,
thread_angle=15,
left_handed=left_handed,
starts=starts,
bevel=bevel,
orient=orient,
align=align
);
}
// Constructs a hex nut for a metric trapezoidal threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Module: metric_trapezoidal_threaded_nut()
// Description:
// Constructs a hex nut for a metric trapezoidal threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Arguments:
// od = diameter of the nut.
// id = diameter of threaded rod to screw onto.
// h = height/thickness of nut.
@ -346,16 +414,40 @@ module metric_trapezoidal_threaded_rod(d=10, l=100, pitch=2, left_handed=false,
// bevel = if true, bevel the thread ends. Default: false
// starts = The number of lead starts. Default = 1
// slop = printer slop calibration to allow for tight fitting of parts. default=0.2
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// metric_trapezoidal_threaded_nut(od=16, d=10, h=10, pitch=2, left_handed=true, bevel=true, $fa=1, $fs=1);
module metric_trapezoidal_threaded_nut(od=17.4, id=10.5, h=10, pitch=3.175, left_handed=false, starts=1, bevel=false, slop=0.2) {
trapezoidal_threaded_nut(od=od, id=id, h=h, pitch=pitch, thread_angle=15, left_handed=left_handed, starts=starts, bevel=bevel, slop=slop);
// metric_trapezoidal_threaded_nut(od=16, id=10, h=10, pitch=2, left_handed=true, bevel=true, $fa=1, $fs=1);
module metric_trapezoidal_threaded_nut(
od=17.4, id=10.5, h=10,
pitch=3.175,
starts=1,
left_handed=false,
bevel=false,
slop=PRINTER_SLOP,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_nut(
od=od, id=id, h=h,
pitch=pitch, thread_angle=15,
left_handed=left_handed,
starts=starts,
bevel=bevel,
slop=slop,
orient=orient,
align=align
);
}
// Section: ACME Trapezoidal Threading
// Constructs an ACME trapezoidal threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Module: acme_threaded_rod()
// Description:
// Constructs an ACME trapezoidal threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Arguments:
// d = Outer diameter of threaded rod.
// l = length of threaded rod.
// pitch = Length between threads.
@ -364,24 +456,40 @@ module metric_trapezoidal_threaded_nut(od=17.4, id=10.5, h=10, pitch=3.175, left
// starts = The number of lead starts. Default = 1
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// orient = Orientation of the rod. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the rod. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// acme_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, $fn=32);
// acme_threaded_rod(d=10, l=40, pitch=2, starts=3, $fa=1, $fs=1);
module acme_threaded_rod(d=10, l=100, pitch=2, thread_angle=14.5, thread_depth=undef, starts=1, left_handed=false, bevel=false) {
module acme_threaded_rod(
d=10, l=100, pitch=2,
thread_angle=14.5,
thread_depth=undef,
starts=1,
left_handed=false,
bevel=false,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_rod(
d=d, l=l, pitch=pitch,
thread_angle=thread_angle,
thread_depth=thread_depth,
starts=starts,
left_handed=left_handed,
bevel=bevel
bevel=bevel,
orient=orient,
align=align
);
}
// Constructs a hex nut for an ACME threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Module: acme_threaded_nut()
// Description:
// Constructs a hex nut for an ACME threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Arguments:
// od = diameter of the nut.
// id = diameter of threaded rod to screw onto.
// h = height/thickness of nut.
@ -391,10 +499,22 @@ module acme_threaded_rod(d=10, l=100, pitch=2, thread_angle=14.5, thread_depth=u
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// slop = printer slop calibration to allow for tight fitting of parts. default=0.2
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// acme_threaded_nut(od=16, id=3/8*25.4, h=8, pitch=1/8*25.4, slop=0.2);
// acme_threaded_nut(od=16, id=10, h=10, pitch=2, starts=3, slop=0.2, $fa=1, $fs=1);
module acme_threaded_nut(od, id, h, pitch, thread_angle=14.5, thread_depth=undef, starts=1, left_handed=false, bevel=false, slop=0.2) {
module acme_threaded_nut(
od, id, h, pitch,
thread_angle=14.5,
thread_depth=undef,
starts=1,
left_handed=false,
bevel=false,
slop=PRINTER_SLOP,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_nut(
od=od, id=id, h=h, pitch=pitch,
thread_depth=thread_depth,
@ -402,30 +522,56 @@ module acme_threaded_nut(od, id, h, pitch, thread_angle=14.5, thread_depth=undef
left_handed=left_handed,
bevel=bevel,
starts=starts,
slop=slop
slop=slop,
orient=orient,
align=align
);
}
// Section: Square Threading
// Constructs a square profile threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Module: square_threaded_rod()
// Description:
// Constructs a square profile threaded screw rod. This method makes
// much smoother threads than the naive linear_extrude method.
// Arguments:
// d = Outer diameter of threaded rod.
// l = length of threaded rod.
// pitch = Length between threads.
// left_handed = if true, create left-handed threads. Default = false
// bevel = if true, bevel the thread ends. Default: false
// starts = The number of lead starts. Default = 1
// orient = Orientation of the rod. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the rod. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// square_threaded_rod(d=10, l=30, pitch=2, starts=2, $fn=32);
module square_threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false, starts=1) {
trapezoidal_threaded_rod(d=d, l=l, pitch=pitch, thread_angle=0, left_handed=left_handed, bevel=bevel, starts=starts);
module square_threaded_rod(
d=10, l=100, pitch=2,
left_handed=false,
bevel=false,
starts=1,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_rod(
d=d, l=l, pitch=pitch,
thread_angle=0,
left_handed=left_handed,
bevel=bevel,
starts=starts,
orient=orient,
align=align
);
}
// Constructs a hex nut for a square profile threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Module: square_threaded_nut()
// Description:
// Constructs a hex nut for a square profile threaded screw rod. This method
// makes much smoother threads than the naive linear_extrude method.
// Arguments:
// od = diameter of the nut.
// id = diameter of threaded rod to screw onto.
// h = height/thickness of nut.
@ -434,16 +580,29 @@ module square_threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false,
// bevel = if true, bevel the thread ends. Default: false
// starts = The number of lead starts. Default = 1
// slop = printer slop calibration to allow for tight fitting of parts. default=0.2
// orient = Orientation of the nut. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
// align = Alignment of the nut. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
// Examples:
// square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, slop=0.15, $fn=32);
module square_threaded_nut(od=17.4, id=10.5, h=10, pitch=3.175, left_handed=false, bevel=false, starts=1, slop=0.2) {
module square_threaded_nut(
od=17.4, id=10.5, h=10,
pitch=3.175,
left_handed=false,
bevel=false,
starts=1,
slop=PRINTER_SLOP,
orient=ORIENT_Z,
align=V_CENTER
) {
trapezoidal_threaded_nut(
od=od, id=id, h=h, pitch=pitch,
thread_angle=0,
left_handed=left_handed,
bevel=bevel,
starts=starts,
slop=slop
slop=slop,
orient=orient,
align=align
);
}

View file

@ -1,5 +1,11 @@
//////////////////////////////////////////////////////////////////////
// Torx driver bits
// LibFile: torx_drive.scad
// Torx driver bits
// To use, add these lines to the top of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/torx_drive.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -33,9 +39,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use <transforms.scad>
use <math.scad>
include <constants.scad>
include <compat.scad>
// Typical outer diameter of Torx profile.
// Section: Functions
// Function: torx_outer_diam()
// Description: Get the typical outer diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_outer_diam(size) = lookup(size, [
[ 6, 1.75],
@ -57,7 +70,9 @@ function torx_outer_diam(size) = lookup(size, [
]);
// Typical inner diameter of Torx profile.
// Function: torx_inner_diam()
// Description: Get typical inner diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_inner_diam(size) = lookup(size, [
[ 6, 1.27],
@ -79,7 +94,9 @@ function torx_inner_diam(size) = lookup(size, [
]);
// Typical drive depth.
// Function: torx_depth()
// Description: Gets typical drive hole depth.
// Arguments:
// size = Torx size.
function torx_depth(size) = lookup(size, [
[ 6, 1.82],
@ -101,7 +118,9 @@ function torx_depth(size) = lookup(size, [
]);
// Minor rounding radius of Torx profile.
// Function: torx_tip_radius()
// Description: Gets minor rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_tip_radius(size) = lookup(size, [
[ 6, 0.132],
@ -123,7 +142,9 @@ function torx_tip_radius(size) = lookup(size, [
]);
// Major rounding radius of Torx profile.
// Function: torx_rounding_radius()
// Description: Gets major rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_rounding_radius(size) = lookup(size, [
[ 6, 0.383],
@ -145,9 +166,14 @@ function torx_rounding_radius(size) = lookup(size, [
]);
// Creates a torx bit 2D profile.
// Section: Modules
// Module: torx_drive2d()
// Description: Creates a torx bit 2D profile.
// Arguments:
// size = Torx size.
// Examples:
// Example(2D):
// torx_drive2d(size=30, $fa=1, $fs=1);
module torx_drive2d(size) {
od = torx_outer_diam(size);
@ -181,15 +207,20 @@ module torx_drive2d(size) {
// Creates a torx bit tip.
// Module: torx_drive()
// Description: Creates a torx bit tip.
// Arguments:
// size = Torx size.
// l = Length of bit.
// center = If true, centers bit vertically.
// Examples:
// torx_drive(size=30, l=10, $fa=1, $fs=1);
module torx_drive(size, l=5, center=undef) {
linear_extrude(height=l, convexity=4, center=center) {
torx_drive2d(size);
module torx_drive(size, l=5, center=undef, orient=ORIENT_Z, align=V_UP) {
od = torx_outer_diam(size);
orient_and_align([od, od, l], orient, align, center) {
linear_extrude(height=l, convexity=4, center=true) {
torx_drive2d(size);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,52 @@
//////////////////////////////////////////////////////////////////////
// LibFile: triangulation.scad
// Functions to triangulate polyhedron faces.
// To use, add the following lines to the beginning of your file:
// ```
// use <BOSL/triangulation.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.
*/
use <math.scad>
// Given an array of vertices (`points`), and a list of indexes into the
// vertex array (`face`), returns the normal vector of the face.
// Section: Functions
// Function: face_normal()
// Description:
// Given an array of vertices (`points`), and a list of indexes into the
// vertex array (`face`), returns the normal vector of the face.
// Arguments:
// points = Array of vertices for the polyhedron.
// face = The face, given as a list of indices into the vertex array `points`.
function face_normal(points, face) =
@ -20,7 +64,10 @@ function face_normal(points, face) =
;
// Returns the index of a convex point on the given face.
// Function: find_convex_vertex()
// Description:
// Returns the index of a convex point on the given face.
// Arguments:
// points = Array of vertices for the polyhedron.
// face = The face, given as a list of indices into the vertex array `points`.
// facenorm = The normal vector of the face.
@ -37,21 +84,25 @@ function find_convex_vertex(points, face, facenorm, i=0) =
;
// Function: point_in_ear()
// Description: Determine if a point is in a clipable convex ear.
// Arguments:
// points = Array of vertices for the polyhedron.
// face = The face, given as a list of indices into the vertex array `points`.
function point_in_ear(points, face, tests, i=0) =
(i<len(face)-1)?
let(
prev=point_in_ear(points, face, tests, i+1),
test=check_point_in_ear(points[face[i]], tests)
test=_check_point_in_ear(points[face[i]], tests)
)
(test>prev[0])? [test, i] : prev
:
[check_point_in_ear(points[face[i]], tests), i]
[_check_point_in_ear(points[face[i]], tests), i]
;
function check_point_in_ear(point, tests) =
// Internal non-exposed function.
function _check_point_in_ear(point, tests) =
let(
result=[
(point*tests[0][0])-tests[0][1],
@ -63,7 +114,9 @@ function check_point_in_ear(point, tests) =
;
// Removes the last item in an array if it is the same as the first item.
// Function: normalize_vertex_perimeter()
// Description: Removes the last item in an array if it is the same as the first item.
// Arguments:
// v = The array to normalize.
function normalize_vertex_perimeter(v) =
(len(v) < 2)? v :
@ -72,8 +125,11 @@ function normalize_vertex_perimeter(v) =
;
// Given a face in a polyhedron, and a vertex in that face, returns true
// if that vertex is the only non-colinear vertex in the face.
// Function: is_only_noncolinear_vertex()
// Description:
// Given a face in a polyhedron, and a vertex in that face, returns true
// if that vertex is the only non-colinear vertex in the face.
// Arguments:
// points = Array of vertices for the polyhedron.
// facelist = The face, given as a list of indices into the vertex array `points`.
// vertex = The index into `facelist`, of the vertex to test.
@ -95,8 +151,11 @@ function is_only_noncolinear_vertex(points, facelist, vertex) =
;
// Given a face in a polyhedron, subdivides the face into triangular faces.
// Returns an array of faces, where each face is a list of vertex indices.
// Function: triangulate_face()
// Description:
// Given a face in a polyhedron, subdivides the face into triangular faces.
// Returns an array of faces, where each face is a list of three vertex indices.
// Arguments:
// points = Array of vertices for the polyhedron.
// face = The face, given as a list of indices into the vertex array `points`.
function triangulate_face(points, face) =
@ -142,8 +201,11 @@ function triangulate_face(points, face) =
;
// Subdivides all faces for the given polyhedron that have more than 3 vertices.
// Returns an array of faces where each face is a list of 3 vertex array indices.
// Function: triangulate_faces()
// Description:
// Subdivides all faces for the given polyhedron that have more than three vertices.
// Returns an array of faces where each face is a list of three vertex array indices.
// Arguments:
// points = Array of vertices for the polyhedron.
// faces = Array of faces for the polyhedron. Each face is a list of 3 or more indices into the `points` array.
function triangulate_faces(points, faces) =

View file

@ -1,5 +1,10 @@
//////////////////////////////////////////////////////////////////////
// Rendering for wiring bundles
// LibFile: wiring.scad
// Rendering for wiring bundles
// To use, include the following line at the top of your file:
// ```
// use <BOSL/wiring.scad>
// ```
//////////////////////////////////////////////////////////////////////
/*
@ -36,43 +41,73 @@ include <paths.scad>
include <beziers.scad>
// Returns an array of 1 or 6 points that form a ring, based on wire diam and ring level.
// Level 0 returns a single point at 0,0. All greater levels return 6 points.
function hex_offset_ring(wirediam, lev=0) =
// Section: Functions
// Function: hex_offset_ring()
// Description:
// Returns a hexagonal ring of points, with a spacing of `d`.
// If `lev=0`, returns a single point at `[0,0]`. All greater
// levels return 6 times `lev` points.
// Usage:
// hex_offset_ring(d, lev)
// Arguments:
// d = Base unit diameter to build rings upon.
// lev = How many rings to produce.
// Example:
// hex_offset_ring(d=1, lev=3); // Returns a hex ring of 18 points.
function hex_offset_ring(d, lev=0) =
(lev == 0)? [[0,0]] : [
for (
sideang = [0:60:359.999],
sidewire = [1:lev]
sidenum = [1:lev]
) [
lev*wirediam*cos(sideang)+sidewire*wirediam*cos(sideang+120),
lev*wirediam*sin(sideang)+sidewire*wirediam*sin(sideang+120)
lev*d*cos(sideang)+sidenum*d*cos(sideang+120),
lev*d*sin(sideang)+sidenum*d*sin(sideang+120)
]
];
// Returns an array of 2D centerpoints for each of a bundle of wires of given diameter.
// The lev and arr variables are used for internal recursion.
function hex_offsets(wires, wirediam, lev=0, arr=[]) =
(len(arr) >= wires)? arr :
// Function: hex_offsets()
// Description:
// Returns the centerpoints for the optimal hexagonal packing
// of at least `n` circular items, of diameter `d`. Will return
// enough points to fill out the last ring, even if that is more
// than `n` points.
// Usage:
// hex_offsets(n, d)
// Arguments:
// n = Number of items to bundle.
// d = How far to space each point away from others.
function hex_offsets(n, d, lev=0, arr=[]) =
(len(arr) >= n)? arr :
hex_offsets(
wires=wires,
wirediam=wirediam,
n=n,
d=d,
lev=lev+1,
arr=concat(arr, hex_offset_ring(wirediam, lev=lev))
arr=concat(arr, hex_offset_ring(d, lev=lev))
);
// Returns a 3D object representing a bundle of wires that follow a given path,
// with the corners filleted to a given radius. There are 17 base wire colors.
// If you have more than 17 wires, colors will get re-used.
// Arguments:
// path: The 3D polyline path that the wire bundle should follow.
// wires: The number of wires in the wiring bundle.
// wirediam: The diameter of each wire in the bundle.
// fillet: The radius that the path corners will be filleted to.
// wirenum: The first wire's offset into the color table.
// bezsteps: The corner fillets in the path will be converted into this number of segments.
// Section: Modules
// Module: wiring()
// Description:
// Returns a 3D object representing a bundle of wires that follow a given path,
// with the corners filleted to a given radius. There are 17 base wire colors.
// If you have more than 17 wires, colors will get re-used.
// Usage:
// wiring(path, wires, [wirediam], [fillet], [wirenum], [bezsteps]);
// Arguments:
// path = The 3D polyline path that the wire bundle should follow.
// wires = The number of wires in the wiring bundle.
// wirediam = The diameter of each wire in the bundle.
// fillet = The radius that the path corners will be filleted to.
// wirenum = The first wire's offset into the color table.
// bezsteps = The corner fillets in the path will be converted into this number of segments.
// Example:
// wiring([[50,0,-50], [50,50,-50], [0,50,-50], [0,0,-50], [0,0,0]], fillet=10, wires=13);
module wiring(path, wires, wirediam=2, fillet=10, wirenum=0, bezsteps=12) {
colors = [