Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Adrian Mariano 2022-08-25 21:12:01 -04:00
commit 800d1b2a62
16 changed files with 1611 additions and 1592 deletions

46
.github/workflows/gen_docs.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: Regenerate Docs
on: [workflow_dispatch]
jobs:
RegenerateDocs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Clone Wiki
uses: actions/checkout@v2
with:
repository: revarbat/BOSL2.wiki
path: BOSL2.wiki
- name: Apt Update
run: sudo apt update
- name: Install Python dev
run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil gifsicle
- name: Install OpenSCAD-DocsGen package.
run: sudo pip3 install openscad-docsgen
- name: Install OpenSCAD
run: |
cd $GITHUB_WORKSPACE
wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
sudo chmod +x /usr/local/bin/openscad
- name: Generate Docs
uses: GabrielBB/xvfb-action@v1.6
env:
OPENSCADPATH: ${{ github.workspace }}/..
with:
run: openscad-docsgen -f
- name: Upload Docs to Wiki
uses: SwiftDocOrg/github-wiki-publish-action@v1
with:
path: "BOSL2.wiki"
env:
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PAT }}

56
.github/workflows/gen_tutorials.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Regenerate Tutorials
on: [workflow_dispatch]
jobs:
RegenerateTutorials:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Clone Wiki
uses: actions/checkout@v2
with:
repository: revarbat/BOSL2.wiki
path: BOSL2.wiki
- name: Apt Update
run: sudo apt update
- name: Install Python dev
run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil gifsicle
- name: Install OpenSCAD-DocsGen package.
run: sudo pip3 install openscad-docsgen
- name: Install OpenSCAD
run: |
cd $GITHUB_WORKSPACE
wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
sudo chmod +x /usr/local/bin/openscad
- name: Tabs Check
run: |
cd $GITHUB_WORKSPACE
./scripts/check_for_tabs.sh
- name: FooTest
env:
OPENSCADPATH: ${{ github.workspace }}/..
run: echo $OPENSCADPATH
- name: Generate Tutorials
uses: GabrielBB/xvfb-action@v1.6
env:
OPENSCADPATH: ${{ github.workspace }}/..
with:
run: openscad-mdimggen -f
- name: Upload Tutorials to Wiki
uses: SwiftDocOrg/github-wiki-publish-action@v1
with:
path: "BOSL2.wiki"
env:
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PAT }}

View file

@ -519,8 +519,8 @@ function affine3d_skew_xy(xa=0, ya=0) =
assert(is_finite(xa)) assert(is_finite(xa))
assert(is_finite(ya)) assert(is_finite(ya))
[ [
[1, 0, tan(xa), 0], [ 1, tan(xa), 0, 0],
[0, 1, tan(ya), 0], [tan(ya), 1, 0, 0],
[ 0, 0, 1, 0], [ 0, 0, 1, 0],
[ 0, 0, 0, 1] [ 0, 0, 0, 1]
]; ];
@ -551,9 +551,9 @@ function affine3d_skew_xz(xa=0, za=0) =
assert(is_finite(xa)) assert(is_finite(xa))
assert(is_finite(za)) assert(is_finite(za))
[ [
[1, tan(xa), 0, 0], [ 1, 0, tan(xa), 0],
[ 0, 1, 0, 0], [ 0, 1, 0, 0],
[0, tan(za), 1, 0], [tan(za), 0, 1, 0],
[ 0, 0, 0, 1] [ 0, 0, 0, 1]
]; ];
@ -584,8 +584,8 @@ function affine3d_skew_yz(ya=0, za=0) =
assert(is_finite(za)) assert(is_finite(za))
[ [
[1, 0, 0, 0], [1, 0, 0, 0],
[tan(ya), 1, 0, 0], [0, 1, tan(ya), 0],
[tan(za), 0, 1, 0], [0, tan(za), 1, 0],
[0, 0, 0, 1] [0, 0, 0, 1]
]; ];

View file

@ -1369,7 +1369,7 @@ function bezier_vnf_degenerate_patch(patch, splinesteps=16, reverse=false, retur
// patch = [for(i=[0:3]) // patch = [for(i=[0:3])
// [for(j=[0:3]) pts1[i]+pts2[j] ] ]; // [for(j=[0:3]) pts1[i]+pts2[j] ] ];
// vnf_polyhedron(bezier_vnf(patch, 163)); // vnf_polyhedron(bezier_vnf(patch, 163));
// uv = [0,.1,.2,.3,,.7,.8,.9,1];//lerpn(0,1,8); // uv = [0,.1,.2,.3,.7,.8,.9,1];//lerpn(0,1,8);
// pts = bezier_patch_points(patch, uv, uv); // pts = bezier_patch_points(patch, uv, uv);
// normals = bezier_patch_normals(patch, uv, uv); // normals = bezier_patch_normals(patch, uv, uv);
// for(i=idx(uv),j=idx(uv)) // for(i=idx(uv),j=idx(uv))

View file

@ -181,9 +181,9 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP)
difference() { difference() {
union() { union() {
if (texture == "knurled") { if (texture == "knurled") {
textured_cylinder(d=w, h=h, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); cyl(d=w, h=h, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT);
} else if (texture == "ribbed") { } else if (texture == "ribbed") {
textured_cylinder(d=w, h=h, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); cyl(d=w, h=h, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT);
} else { } else {
cyl(d=w, l=tamper_ring_h+wall, anchor=BOTTOM); cyl(d=w, l=tamper_ring_h+wall, anchor=BOTTOM);
} }
@ -362,9 +362,9 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP)
difference() { difference() {
union() { union() {
if (texture == "knurled") { if (texture == "knurled") {
textured_cylinder(d=w, h=11.2+wall, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); cyl(d=w, h=11.2+wall, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT);
} else if (texture == "ribbed") { } else if (texture == "ribbed") {
textured_cylinder(d=w, h=11.2+wall, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); cyl(d=w, h=11.2+wall, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT);
} else { } else {
cyl(d=w, l=11.2+wall, anchor=BOTTOM); cyl(d=w, l=11.2+wall, anchor=BOTTOM);
} }
@ -567,9 +567,9 @@ module generic_bottle_cap(
// thickness so the wall+texture are the specified wall thickness. That // thickness so the wall+texture are the specified wall thickness. That
// seems wrong so this does specified thickness+texture // seems wrong so this does specified thickness+texture
if (texture == "knurled") { if (texture == "knurled") {
textured_cylinder(d=w + 1.5*diamMagMult, l=h, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); cyl(d=w + 1.5*diamMagMult, l=h, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT);
} else if (texture == "ribbed") { } else if (texture == "ribbed") {
textured_cylinder(d=w + 1.5*diamMagMult, l=h, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); cyl(d=w + 1.5*diamMagMult, l=h, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT);
} else { } else {
cyl(d = w, l = h, anchor = BOTTOM); cyl(d = w, l = h, anchor = BOTTOM);
} }

View file

@ -85,7 +85,6 @@
// joint_color = If given, sets the color of the joints. Overrides `color=` and `dots_color=`. // joint_color = If given, sets the color of the joints. Overrides `color=` and `dots_color=`.
// dots_color = If given, sets the color of the endcaps and joints. Overrides `color=`. // dots_color = If given, sets the color of the endcaps and joints. Overrides `color=`.
// convexity = Max number of times a line could intersect a wall of an endcap. // convexity = Max number of times a line could intersect a wall of an endcap.
// hull = If true, use `hull()` to make higher quality joints between segments, at the cost of being much slower. Default: true
// Example(2D): Drawing a Path // Example(2D): Drawing a Path
// path = [[0,100], [100,100], [200,0], [100,-100], [100,0]]; // path = [[0,100], [100,100], [200,0], [100,-100], [100,0]];
// stroke(path, width=20); // stroke(path, width=20);
@ -157,7 +156,7 @@ function stroke(
endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle, endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle,
endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color, endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color,
trim, trim1, trim2, trim, trim1, trim2,
convexity=10, hull=true convexity=10
) = no_function("stroke"); ) = no_function("stroke");
@ -170,7 +169,7 @@ module stroke(
endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle, endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle,
endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color, endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color,
trim, trim1, trim2, trim, trim1, trim2,
convexity=10, hull=true convexity=10
) { ) {
no_children($children); no_children($children);
module setcolor(clr) { module setcolor(clr) {
@ -359,7 +358,7 @@ module stroke(
for (i = [1:1:len(path2)-2]) { for (i = [1:1:len(path2)-2]) {
$fn = quantup(segs(widths[i]/2),4); $fn = quantup(segs(widths[i]/2),4);
translate(path2[i]) { translate(path2[i]) {
if (joints != undef) { if (joints != undef && joints != "round") {
joint_shape = _shape_path( joint_shape = _shape_path(
joints, width[i], joints, width[i],
joint_width, joint_width,
@ -372,18 +371,21 @@ module stroke(
? rot(from=BACK,to=v1) ? rot(from=BACK,to=v1)
: zrot(joint_angle); : zrot(joint_angle);
multmatrix(mat) polygon(joint_shape); multmatrix(mat) polygon(joint_shape);
} else if (hull) {
hull() {
rot(from=BACK, to=path2[i]-path2[i-1])
circle(d=widths[i]);
rot(from=BACK, to=path2[i+1]-path2[i])
circle(d=widths[i]);
}
} else { } else {
rot(from=BACK, to=path2[i]-path2[i-1]) v1 = path2[i] - path2[i-1];
circle(d=widths[i]); v2 = path2[i+1] - path2[i];
rot(from=BACK, to=path2[i+1]-path2[i]) ang = modang(v_theta(v2) - v_theta(v1));
circle(d=widths[i]); pv1 = rot(-90, p=unit(v1,BACK));
pv2 = rot(-90, p=unit(v2,BACK));
if (!approx(ang,0)) {
if (ang>=0) {
rot(from=RIGHT, to=pv1)
arc(d=widths[i], angle=ang, wedge=true);
} else {
rot(from=RIGHT, to=-pv2)
arc(d=widths[i], angle=-ang, wedge=true);
}
}
} }
} }
} }
@ -439,7 +441,7 @@ module stroke(
for (i = [1:1:len(path2)-2]) { for (i = [1:1:len(path2)-2]) {
$fn = sides[i]; $fn = sides[i];
translate(path2[i]) { translate(path2[i]) {
if (joints != undef) { if (joints != undef && joints != "round") {
joint_shape = _shape_path( joint_shape = _shape_path(
joints, width[i], joints, width[i],
joint_width, joint_width,
@ -462,21 +464,18 @@ module stroke(
} }
} }
} }
} else if (hull) {
hull(){
multmatrix(rotmats[i]) {
sphere(d=widths[i],style="aligned");
}
multmatrix(rotmats[i-1]) {
sphere(d=widths[i],style="aligned");
}
}
} else { } else {
multmatrix(rotmats[i]) { corner = select(path2,i-1,i+1);
sphere(d=widths[i],style="aligned"); axis = vector_axis(corner);
ang = vector_angle(corner);
if (!approx(ang,0)) {
frame_map(x=path2[i-1]-path2[i], z=-axis) {
zrot(90-0.5) {
rotate_extrude(angle=180-ang+1) {
arc(d=widths[i], start=-90, angle=180);
}
}
} }
multmatrix(rotmats[i-1]) {
sphere(d=widths[i],style="aligned");
} }
} }
} }
@ -552,6 +551,9 @@ module stroke(
// --- // ---
// width = The width of the dashed line to draw. Module only. Default: 1 // width = The width of the dashed line to draw. Module only. Default: 1
// closed = If true, treat path as a closed polygon. Default: false // closed = If true, treat path as a closed polygon. Default: false
// fit = If true, shrink or stretch the dash pattern so that the path ends ofter a logical dash. Default: true
// roundcaps = (Module only) If true, draws dashes with rounded caps. This often looks better. Default: true
// mindash = (Function only) Specifies the minimal dash length to return at the end of a path when fit is false. Default: 0.5
// Example(2D): Open Path // Example(2D): Open Path
// path = [for (a=[-180:10:180]) [a/3,20*sin(a)]]; // path = [for (a=[-180:10:180]) [a/3,20*sin(a)]];
// dashed_stroke(path, [3,2], width=1); // dashed_stroke(path, [3,2], width=1);
@ -562,31 +564,44 @@ module stroke(
// Example(FlatSpin,VPD=250): 3D Dashed Path // Example(FlatSpin,VPD=250): 3D Dashed Path
// path = [for (a=[-180:5:180]) [a/3, 20*cos(3*a), 20*sin(3*a)]]; // path = [for (a=[-180:5:180]) [a/3, 20*cos(3*a), 20*sin(3*a)]];
// dashed_stroke(path, [3,2], width=1); // dashed_stroke(path, [3,2], width=1);
function dashed_stroke(path, dashpat=[3,3], closed=false) = function dashed_stroke(path, dashpat=[3,3], closed=false, fit=true, mindash=0.5) =
is_region(path) ? [for(p=path) each dashed_stroke(p,dashpat,closed=true)] : is_region(path) ? [
for (p = path)
each dashed_stroke(p, dashpat, closed=true, fit=fit)
] :
let( let(
path = closed? close_path(path) : path, path = closed? close_path(path) : path,
dashpat = len(dashpat)%2==0? dashpat : concat(dashpat,[0]), dashpat = len(dashpat)%2==0? dashpat : concat(dashpat,[0]),
plen = path_length(path), plen = path_length(path),
dlen = sum(dashpat), dlen = sum(dashpat),
doff = cumsum(dashpat), doff = cumsum(dashpat),
reps = floor(plen / dlen), freps = plen / dlen,
step = plen / reps, reps = max(1, fit? round(freps) : floor(freps)),
tlen = !fit? plen :
reps * dlen + (closed? 0 : dashpat[0]),
sc = plen / tlen,
cuts = [ cuts = [
for (i=[0:1:reps-1], off=doff) for (i = [0:1:reps], off = doff*sc)
let (st=i*step, x=st+off) let (x = i*dlen*sc + off)
if (x > 0 && x < plen) x if (x > 0 && x < plen) x
], ],
dashes = path_cut(path, cuts, closed=false), dashes = path_cut(path, cuts, closed=false),
evens = [for (i=idx(dashes)) if (i%2==0) dashes[i]] dcnt = len(dashes),
evens = [
for (i = idx(dashes))
if (i % 2 == 0)
let( dash = dashes[i] )
if (i < dcnt-1 || path_length(dash) > mindash)
dashes[i]
]
) evens; ) evens;
module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) { module dashed_stroke(path, dashpat=[3,3], width=1, closed=false, fit=true, roundcaps=false) {
no_children($children); no_children($children);
segs = dashed_stroke(path, dashpat=dashpat*width, closed=closed); segs = dashed_stroke(path, dashpat=dashpat*width, closed=closed, fit=fit, mindash=0.5*width);
for (seg = segs) for (seg = segs)
stroke(seg, width=width, endcaps=false); stroke(seg, width=width, endcaps=roundcaps? "round" : false);
} }

View file

@ -1069,7 +1069,7 @@ function circle_circle_intersection(r1, cp1, r2, cp2, eps=EPSILON, d1, d2) =
// for (i = [0:1:5]) { // for (i = [0:1:5]) {
// crn = select(path, i*2-1, i*2+1); // crn = select(path, i*2-1, i*2+1);
// ci = circle_2tangents(5, crn[0], crn[1], crn[2]); // ci = circle_2tangents(5, crn[0], crn[1], crn[2]);
// move(ci[0]) cyl(h=10,r=5,,orient=ci[1]); // move(ci[0]) cyl(h=10,r=5,orient=ci[1]);
// } // }
function circle_2tangents(r, pt1, pt2, pt3, tangents=false, d) = function circle_2tangents(r, pt1, pt2, pt3, tangents=false, d) =
let(r = get_radius(r=r, d=d, dflt=undef)) let(r = get_radius(r=r, d=d, dflt=undef))

View file

@ -36,7 +36,7 @@
// #chamfer_edge_mask(l=50, chamfer=10, orient=RIGHT); // #chamfer_edge_mask(l=50, chamfer=10, orient=RIGHT);
// } // }
// Example: Masking by Attachment // Example: Masking by Attachment
// diff("mask") // diff()
// cube(50, center=true) { // cube(50, center=true) {
// edge_mask(TOP+RIGHT) // edge_mask(TOP+RIGHT)
// #chamfer_edge_mask(l=50, chamfer=10); // #chamfer_edge_mask(l=50, chamfer=10);
@ -71,7 +71,7 @@ module chamfer_edge_mask(l=1, chamfer=1, excess=0.1, anchor=CENTER, spin=0, orie
// move(25*[1,-1,1]) #chamfer_corner_mask(chamfer=10); // move(25*[1,-1,1]) #chamfer_corner_mask(chamfer=10);
// } // }
// Example: Masking by Attachment // Example: Masking by Attachment
// diff("mask") // diff()
// cuboid(100, chamfer=20, trimcorners=false) { // cuboid(100, chamfer=20, trimcorners=false) {
// corner_mask(TOP+FWD+RIGHT) // corner_mask(TOP+FWD+RIGHT)
// chamfer_corner_mask(chamfer=20); // chamfer_corner_mask(chamfer=20);
@ -166,12 +166,12 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE
// #rounding_edge_mask(l=50, r1=25, r2=10, orient=UP, anchor=BOTTOM); // #rounding_edge_mask(l=50, r1=25, r2=10, orient=UP, anchor=BOTTOM);
// } // }
// Example: Masking by Attachment // Example: Masking by Attachment
// diff("mask") // diff()
// cube(100, center=true) // cube(100, center=true)
// edge_mask(FRONT+RIGHT) // edge_mask(FRONT+RIGHT)
// #rounding_edge_mask(l=$parent_size.z+0.01, r=25); // #rounding_edge_mask(l=$parent_size.z+0.01, r=25);
// Example: Multiple Masking by Attachment // Example: Multiple Masking by Attachment
// diff("mask") // diff()
// cube([80,90,100], center=true) { // cube([80,90,100], center=true) {
// let(p = $parent_size*1.01) { // let(p = $parent_size*1.01) {
// edge_mask(TOP) // edge_mask(TOP)
@ -237,7 +237,7 @@ module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, sp
// #rounding_corner_mask(r=20, spin=90); // #rounding_corner_mask(r=20, spin=90);
// } // }
// Example: Masking by Attachment // Example: Masking by Attachment
// diff("mask") // diff()
// cube(size=[50, 60, 70]) { // cube(size=[50, 60, 70]) {
// corner_mask(TOP) // corner_mask(TOP)
// #rounding_corner_mask(r=20); // #rounding_corner_mask(r=20);
@ -396,10 +396,11 @@ module rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=U
// up(50) rounding_cylinder_mask(r=50, rounding=10); // up(50) rounding_cylinder_mask(r=50, rounding=10);
// } // }
// Example: Masking by Attachment // Example: Masking by Attachment
// diff("mask") // diff()
// cyl(h=30, d=30) { // cyl(h=30, d=30) {
// attach(TOP) // attach(TOP)
// #tag("mask")rounding_cylinder_mask(d=30, rounding=5); // #tag("remove")
// rounding_cylinder_mask(d=30, rounding=5);
// } // }
function rounding_cylinder_mask(r, rounding, d) = no_function("rounding_cylinder_mask"); function rounding_cylinder_mask(r, rounding, d) = no_function("rounding_cylinder_mask");
module rounding_cylinder_mask(r, rounding, d) module rounding_cylinder_mask(r, rounding, d)
@ -475,7 +476,7 @@ module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, ori
// Example(VPD=50,VPR=[55,0,120]): // Example(VPD=50,VPR=[55,0,120]):
// teardrop_edge_mask(l=20, r=10, angle=40); // teardrop_edge_mask(l=20, r=10, angle=40);
// Example(VPD=300,VPR=[75,0,25]): // Example(VPD=300,VPR=[75,0,25]):
// diff("mask") // diff()
// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { // cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
// edge_mask(BOT) // edge_mask(BOT)
// teardrop_edge_mask(l=max($parent_size)+1, r=10, angle=40); // teardrop_edge_mask(l=max($parent_size)+1, r=10, angle=40);
@ -512,7 +513,7 @@ module teardrop_edge_mask(l, r, angle, excess=0.1, d)
// Example: // Example:
// teardrop_corner_mask(r=20, angle=40); // teardrop_corner_mask(r=20, angle=40);
// Example: // Example:
// diff("mask") // diff()
// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { // cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
// edge_profile(BOT) // edge_profile(BOT)
// mask2d_teardrop(r=10, angle=40); // mask2d_teardrop(r=10, angle=40);

View file

@ -5,140 +5,70 @@
// include <BOSL2/std.scad> // include <BOSL2/std.scad>
// include <BOSL2/nema_steppers.scad> // include <BOSL2/nema_steppers.scad>
// FileGroup: Parts // FileGroup: Parts
// FileSummary: Mounting holes for NEMA motors, and simple motor models. // FileSummary: NEMA motor mounts and stepper motor models.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// 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],
[17.0, 42.3],
[23.0, 57.0],
[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],
[17.0, 2.0],
[23.0, 1.6],
[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],
[17.0, 22.0],
[23.0, 38.1],
[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],
[17.0, 30.99],
[23.0, 47.14],
[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],
[17.0, 3.0],
[23.0, 5.1],
[34.0, 5.5],
]);
// Function: nema_motor_screw_depth()
// Description: Gets mount screw-hole 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],
[17.0, 4.5],
[23.0, 4.8],
[34.0, 9.0],
]);
// Section: Motor Models // Section: Motor Models
// Module: nema11_stepper() // Module: nema_stepper_motor()
// Description: Creates a model of a NEMA 11 stepper motor. // Usage:
// nema_stepper_motor(size, h, shaft_len, ...) [attachments];
// Topics: Parts, Motors
// Description:
// Creates a model of a NEMA standard stepper motor.
// Arguments: // Arguments:
// size = The NEMA standard size of the stepper motor.
// h = Length of motor body. Default: 24mm // 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 // shaft_len = Length of shaft protruding out the top of the stepper motor. Default: 20mm
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // ---
// details = If false, creates a very rough motor shape, suitable for using as a mask. Default: true
// atype = The attachment set type to use when anchoring. Default: `"body"`
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `TOP`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Extra Anchors: // Anchor Types:
// "shaft-top" = The top of the shaft. // "shaft" = Anchor relative to the shaft.
// "shaft-middle" = The middle of the shaft. // "plinth" = Anchor relative to the plinth.
// "shaft-bottom" = The bottom of the shaft, 0.1mm above the plinth. // "body" = Anchor relative to the motor body.
// "plinth-top" = The top of the plinth. // "screws" = Anchor relative to the screw hole centers. ie: TOP+RIGHT+FRONT is the center-top of the front-right screwhole.
// "screw1" = The screw-hole in the X+Y+ quadrant. // See Also: nema_stepper_motor(), nema_mount_mask()
// "screw2" = The screw-hole in the X-Y+ quadrant. // Examples:
// "screw3" = The screw-hole in the X-Y- quadrant. // nema_stepper_motor(size=8, h=24, shaft_len=15);
// "screw4" = The screw-hole in the X+Y- quadrant. // nema_stepper_motor(size=11, h=24, shaft_len=20);
// Example: // nema_stepper_motor(size=17, h=40, shaft_len=30);
// nema11_stepper(); // nema_stepper_motor(size=23, h=50, shaft_len=40);
module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP) // nema_stepper_motor(size=23, h=50, shaft_len=40, details=false);
module nema_stepper_motor(size=17, h=24, shaft_len=20, details=true, atype="body", anchor=TOP, spin=0, orient=UP)
{ {
size = 11; info = nema_motor_info(size);
motor_width = nema_motor_width(size); motor_width = info[0];
plinth_height = nema_motor_plinth_height(size); plinth_height = info[1];
plinth_diam = nema_motor_plinth_diam(size); plinth_diam = info[2];
screw_spacing = nema_motor_screw_spacing(size); screw_spacing = info[3];
screw_size = nema_motor_screw_size(size); screw_size = info[4];
screw_depth = nema_motor_screw_depth(size); screw_depth = info[5];
shaft_diam = info[6];
anchors = [ geom = atype=="shaft"? attach_geom(r=shaft_diam/2, h=shaft_len-plinth_height, cp=[0,0,h/2+plinth_height/2+shaft_len/2]) :
named_anchor("shaft-top", [0,0,h/2+shaft_len]), atype=="plinth"? attach_geom(r=plinth_diam/2, h=plinth_height, cp=[0,0,h/2+plinth_height/2]) :
named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), atype=="body"? attach_geom(size=[motor_width, motor_width, h]) :
named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), atype=="screws"? attach_geom(size=[screw_spacing, screw_spacing, screw_depth], cp=[0,0,h/2-screw_depth/2]) :
named_anchor("plinth-top", [0,0,h/2+plinth_height]), assert(in_list(atype, ["shaft", "plinth", "body", "screws"]));
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), attachable(anchor,spin,orient, geom=geom) {
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), up(h/2) {
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), if (details == false) {
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), slop = get_slop();
]; color([0.4, 0.4, 0.4])
attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { cuboid(size=[motor_width+2*slop, motor_width+2*slop, h+slop], anchor=TOP);
up(h/2) color([0.6, 0.6, 0.6])
union() { cylinder(h=plinth_height+slop, d=plinth_diam+2*slop);
color("silver")
cylinder(h=shaft_len+slop, d=shaft_diam+2*slop, $fn=max(12,segs(shaft_diam/2)));
} else if (size < 23) {
difference() { difference() {
color([0.4, 0.4, 0.4]) color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges="Z", anchor=TOP); cuboid(size=[motor_width, motor_width, h], chamfer=size>=8? 2 : 0.5, edges="Z", anchor=TOP);
color("silver") color("silver")
xcopies(screw_spacing) xcopies(screw_spacing)
ycopies(screw_spacing) ycopies(screw_spacing)
@ -147,292 +77,33 @@ module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP
color([0.6, 0.6, 0.6]) { color([0.6, 0.6, 0.6]) {
difference() { difference() {
cylinder(h=plinth_height, d=plinth_diam); cylinder(h=plinth_height, d=plinth_diam);
cyl(h=plinth_height*3, d=shaft+0.75); cyl(h=plinth_height*3, d=shaft_diam+0.75);
} }
} }
color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); color("silver") cylinder(h=shaft_len, d=shaft_diam, $fn=max(12,segs(shaft_diam/2)));
} } else {
children();
}
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Extra Anchors:
// "shaft-top" = The top of the shaft.
// "shaft-middle" = The middle of the shaft.
// "shaft-bottom" = The bottom of the shaft, 0.1mm above the plinth.
// "plinth-top" = The top of the plinth.
// "screw1" = The screw-hole in the X+Y+ quadrant.
// "screw2" = The screw-hole in the X-Y+ quadrant.
// "screw3" = The screw-hole in the X-Y- quadrant.
// "screw4" = The screw-hole in the X+Y- quadrant.
// Example:
// nema14_stepper();
module nema14_stepper(h=24, shaft=5, shaft_len=24, anchor=TOP, spin=0, orient=UP)
{
size = 14;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
anchors = [
named_anchor("shaft-top", [0,0,h/2+shaft_len]),
named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]),
named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]),
named_anchor("plinth-top", [0,0,h/2+plinth_height]),
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]),
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]),
];
attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) {
up(h/2)
union() {
difference() { difference() {
union() {
color([0.4, 0.4, 0.4]) color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges="Z", anchor=TOP); cuboid([motor_width, motor_width, h], rounding=screw_size, edges="Z", anchor=TOP);
color("silver")
xcopies(screw_spacing)
ycopies(screw_spacing)
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
}
color([0.6, 0.6, 0.6]) { color([0.6, 0.6, 0.6]) {
difference() { difference() {
cylinder(h=plinth_height, d=plinth_diam); cylinder(h=plinth_height, d=plinth_diam);
cyl(h=plinth_height*3, d=shaft+0.75); cyl(h=plinth_height*3, d=shaft_diam+0.75);
} }
} }
color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
}
children();
}
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Extra Anchors:
// "shaft-top" = The top of the shaft.
// "shaft-middle" = The middle of the shaft.
// "shaft-bottom" = The bottom of the shaft, 0.1mm above the plinth.
// "plinth-top" = The top of the plinth.
// "screw1" = The screw-hole in the X+Y+ quadrant.
// "screw2" = The screw-hole in the X-Y+ quadrant.
// "screw3" = The screw-hole in the X-Y- quadrant.
// "screw4" = The screw-hole in the X+Y- quadrant.
// Example:
// nema17_stepper();
module nema17_stepper(h=34, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP)
{
size = 17;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
anchors = [
named_anchor("shaft-top", [0,0,h/2+shaft_len]),
named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]),
named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]),
named_anchor("plinth-top", [0,0,h/2+plinth_height]),
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]),
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]),
];
attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) {
up(h/2)
union() {
difference() {
color([0.4, 0.4, 0.4])
cuboid([motor_width, motor_width, h], chamfer=2, edges="Z", anchor=TOP);
color("silver") color("silver")
xcopies(screw_spacing) cylinder(h=shaft_len, d=shaft_diam, $fn=max(12,segs(shaft_diam/2)));
ycopies(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([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=BACK, $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);
}
}
}
}
}
children();
}
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Extra Anchors:
// "shaft-top" = The top of the shaft.
// "shaft-middle" = The middle of the shaft.
// "shaft-bottom" = The bottom of the shaft, 0.1mm above the plinth.
// "plinth-top" = The top of the plinth.
// "screw1" = The screw-hole in the X+Y+ quadrant.
// "screw2" = The screw-hole in the X-Y+ quadrant.
// "screw3" = The screw-hole in the X-Y- quadrant.
// "screw4" = The screw-hole in the X+Y- quadrant.
// Example:
// nema23_stepper();
module nema23_stepper(h=50, shaft=6.35, shaft_len=25, anchor=TOP, spin=0, orient=UP)
{
size = 23;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
anchors = [
named_anchor("shaft-top", [0,0,h/2+shaft_len]),
named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]),
named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]),
named_anchor("plinth-top", [0,0,h/2+plinth_height]),
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]),
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]),
];
attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) {
up(h/2)
difference() {
union() {
color([0.4, 0.4, 0.4])
cuboid([motor_width, motor_width, h], chamfer=2, edges="Z", anchor=TOP);
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]) { color([0.4, 0.4, 0.4]) {
xcopies(screw_spacing) { xcopies(screw_spacing) {
ycopies(screw_spacing) { ycopies(screw_spacing) {
cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2))); cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2)));
down(screw_depth) cuboid([screw_inset, screw_inset, h], anchor=TOP); down(screw_depth) cuboid([screw_size*2, screw_size*2, h], anchor=TOP);
} }
} }
} }
} }
children();
}
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Extra Anchors:
// "shaft-top" = The top of the shaft.
// "shaft-middle" = The middle of the shaft.
// "shaft-bottom" = The bottom of the shaft, 0.1mm above the plinth.
// "plinth-top" = The top of the plinth.
// "screw1" = The screw-hole in the X+Y+ quadrant.
// "screw2" = The screw-hole in the X-Y+ quadrant.
// "screw3" = The screw-hole in the X-Y- quadrant.
// "screw4" = The screw-hole in the X+Y- quadrant.
// Example:
// nema34_stepper();
module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient=UP)
{
size = 34;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
anchors = [
named_anchor("shaft-top", [0,0,h/2+shaft_len]),
named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]),
named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]),
named_anchor("plinth-top", [0,0,h/2+plinth_height]),
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]),
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]),
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]),
];
attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) {
up(h/2)
difference() {
union() {
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges="Z", anchor=TOP);
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]) {
xcopies(screw_spacing) {
ycopies(screw_spacing) {
cylinder(d=screw_size, h=screw_depth*3, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth) cube([screw_inset, screw_inset, h], anchor=TOP);
}
}
} }
} }
children(); children();
@ -444,63 +115,59 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient
// Section: Masking Modules // Section: Masking Modules
// Module: nema_mount_mask()
// Module: nema_mount_holes() // Usage:
// nema_mount_mask(size, depth, l, ...);
// Topics: Parts, Motors
// Description: Creates a mask to use when making standard NEMA stepper motor mounts. // Description: Creates a mask to use when making standard NEMA stepper motor mounts.
// Arguments: // Arguments:
// size = The standard NEMA motor size to make a mount for. // size = The standard NEMA motor size to make a mount for.
// depth = The thickness of the mounting hole mask. Default: 5 // depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5 // l = The length of the slots, for making an adjustable motor mount. Default: 5
// ---
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// $slop = The printer-specific slop value to make parts fit just right. // $slop = The printer-specific slop value to make parts fit just right.
// Extra Anchors: // Anchor Types:
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant. // "full" = Anchor relative the full mask.
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant. // "screws" = Anchor relative to the screw hole centers. ie: TOP+RIGHT+FRONT is the center-top of the front-right screwhole.
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant. // See Also: nema_stepper_motor(), nema_mount_mask()
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant. // Examples:
// Example: // nema_mount_mask(size=14, depth=5, l=5);
// nema_mount_holes(size=14, depth=5, l=5); // nema_mount_mask(size=17, depth=5, l=5);
// Example: // nema_mount_mask(size=17, depth=5, l=0);
// nema_mount_holes(size=17, depth=5, l=5); module nema_mount_mask(size, depth=5, l=5, atype="full", anchor=CENTER, spin=0, orient=UP)
// Example:
// nema_mount_holes(size=17, depth=5, l=0);
module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{ {
motor_width = nema_motor_width(size); slop = get_slop();
plinth_diam = nema_motor_plinth_diam(size)+get_slop(); info = nema_motor_info(size);
screw_spacing = nema_motor_screw_spacing(size); motor_width = info[0];
screw_size = nema_motor_screw_size(size)+get_slop(); plinth_height = info[1];
plinth_diam = info[2] + slop;
anchors = [ screw_spacing = info[3];
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]), screw_size = info[4] + slop;
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]), screw_depth = info[5];
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]), shaft_diam = info[6];
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]),
];
screwfn = quantup(max(8,segs(screw_size/2)),4); screwfn = quantup(max(8,segs(screw_size/2)),4);
plinthfn = quantup(max(8,segs(plinth_diam/2)),4); plinthfn = quantup(max(8,segs(plinth_diam/2)),4);
s = [screw_spacing+screw_size, screw_spacing+screw_size+l, depth]; s = atype=="full"? [screw_spacing+screw_size, screw_spacing+screw_size+l, depth] :
attachable(anchor,spin,orient, size=s, anchors=anchors) { atype=="screws"? [screw_spacing, screw_spacing, depth] :
assert(in_list(atype, ["full", "screws"]));
attachable(anchor,spin,orient, size=s) {
union() { union() {
xcopies(screw_spacing) { xcopies(screw_spacing) {
ycopies(screw_spacing) { ycopies(screw_spacing) {
if (l > 0) { if (l > 0) {
union() {
ycopies(l) cyl(h=depth, d=screw_size, $fn=screwfn); ycopies(l) cyl(h=depth, d=screw_size, $fn=screwfn);
cube([screw_size, l, depth], center=true); cube([screw_size, l, depth], center=true);
}
} else { } else {
cyl(h=depth, d=screw_size, $fn=screwfn); cyl(h=depth, d=screw_size, $fn=screwfn);
} }
} }
} }
if (l > 0) { if (l > 0) {
union () {
ycopies(l) cyl(h=depth, d=plinth_diam, $fn=plinthfn); ycopies(l) cyl(h=depth, d=plinth_diam, $fn=plinthfn);
cube([plinth_diam, l, depth], center=true); cube([plinth_diam, l, depth], center=true);
}
} else { } else {
cyl(h=depth, d=plinth_diam, $fn=plinthfn); cyl(h=depth, d=plinth_diam, $fn=plinthfn);
} }
@ -511,128 +178,40 @@ module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
// Module: nema11_mount_holes() // Section: Functions
// Description: Creates a mask to use when making NEMA 11 stepper motor mounts.
// Function: nema_motor_info()
// Usage:
// info = nema_motor_info(size);
// Description:
// Gets various dimension info for a NEMA stepper motor of a specific size.
// Returns a list of scalar values, containing, in order:
// - MOTOR_WIDTH: The full width and length of the motor.
// - PLINTH_HEIGHT: The height of the circular plinth on the face of the motor.
// - PLINTH_DIAM: The diameter of the circular plinth on the face of the motor.
// - SCREW_SPACING: The spacing between screwhole centers in both X and Y axes.
// - SCREW_SIZE: The diameter of the screws.
// - SCREW_DEPTH: The depth of the screwholes.
// - SHAFT_DIAM: The diameter of the motor shaft.
// Arguments: // Arguments:
// depth = The thickness of the mounting hole mask. Default: 5 // size = The standard NEMA motor size.
// l = The length of the slots, for making an adjustable motor mount. Default: 5 function nema_motor_info(size) =
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` let(
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` info_arr = [
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` [ 6, [ 14.0, 1.50, 11.0, 11.50, 1.6, 2.5, 4.00]],
// $slop = The printer-specific slop value to make parts fit just right. [ 8, [ 20.3, 1.50, 16.0, 15.40, 2.0, 2.5, 4.00]],
// Extra Anchors: [11, [ 28.2, 1.50, 22.0, 23.11, 2.6, 3.0, 5.00]],
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant. [14, [ 35.2, 2.00, 22.0, 26.00, 3.0, 4.5, 5.00]],
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant. [17, [ 42.3, 2.00, 22.0, 31.00, 3.0, 4.5, 5.00]],
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant. [23, [ 57.0, 1.60, 38.1, 47.00, 5.1, 4.8, 6.35]],
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant. [34, [ 86.0, 2.00, 73.0, 69.60, 6.5, 10.0, 14.00]],
// Example: [42, [110.0, 1.50, 55.5, 88.90, 8.5, 12.7, 19.00]],
// nema11_mount_holes(depth=5, l=5); ],
// Example: found = [for(info=info_arr) if(info[0]==size) info[1]]
// nema11_mount_holes(depth=5, l=0); )
module nema11_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) assert(found, "Unsupported NEMA size.")
{ found[0];
nema_mount_holes(size=11, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children();
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// $slop = The printer-specific slop value to make parts fit just right.
// Extra Anchors:
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant.
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant.
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant.
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant.
// Example:
// nema14_mount_holes(depth=5, l=5);
// Example:
// nema14_mount_holes(depth=5, l=0);
module nema14_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{
nema_mount_holes(size=14, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children();
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// $slop = The printer-specific slop value to make parts fit just right.
// Extra Anchors:
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant.
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant.
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant.
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant.
// Example:
// nema17_mount_holes(depth=5, l=5);
// Example:
// nema17_mount_holes(depth=5, l=0);
module nema17_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{
nema_mount_holes(size=17, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children();
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// $slop = The printer-specific slop value to make parts fit just right.
// Extra Anchors:
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant.
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant.
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant.
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant.
// Example:
// nema23_mount_holes(depth=5, l=5);
// Example:
// nema23_mount_holes(depth=5, l=0);
module nema23_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{
nema_mount_holes(size=23, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children();
}
// 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
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// $slop = The printer-specific slop value to make parts fit just right.
// Extra Anchors:
// "screw1" = The center top of the screw hole/slot in the X+Y+ quadrant.
// "screw2" = The center top of the screw hole/slot in the X-Y+ quadrant.
// "screw3" = The center top of the screw hole/slot in the X-Y- quadrant.
// "screw4" = The center top of the screw hole/slot in the X+Y- quadrant.
// Example:
// nema34_mount_holes(depth=5, l=5);
// Example:
// nema34_mount_holes(depth=5, l=0);
module nema34_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{
nema_mount_holes(size=34, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children();
}

View file

@ -143,8 +143,8 @@ function phillips_diam(size, depth) =
// Examples: // Examples:
// torx_mask(size=30, l=10, $fa=1, $fs=1); // torx_mask(size=30, l=10, $fa=1, $fs=1);
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) { module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_diam(size); od = torx_diam(size);
anchor = get_anchor(anchor, center, BOT, BOT);
attachable(anchor,spin,orient, d=od, l=l) { attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) { linear_extrude(height=l, convexity=4, center=true) {
torx_mask2d(size); torx_mask2d(size);
@ -165,10 +165,11 @@ module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
// torx_mask2d(size=30, $fa=1, $fs=1); // torx_mask2d(size=30, $fa=1, $fs=1);
module torx_mask2d(size) { module torx_mask2d(size) {
no_children($children); no_children($children);
od = torx_diam(size); info = torx_info(size);
id = _torx_inner_diam(size); od = info[0];
tip = _torx_tip_radius(size); id = info[1];
rounding = _torx_rounding_radius(size); tip = info[3];
rounding = info[4];
base = od - 2*tip; base = od - 2*tip;
$fn = quantup(segs(od/2),12); $fn = quantup(segs(od/2),12);
difference() { difference() {
@ -195,197 +196,70 @@ module torx_mask2d(size) {
} }
// Function: torx_info()
// Usage:
// info = torx_info(size);
// Description:
// Get the typical dimensional info for a given Torx size.
// Returns a list containing, in order:
// - Outer Diameter
// - Inner Diameter
// - Drive Hole Depth
// - External Tip Rounding Radius
// - Inner Rounding Radius
// Arguments:
// size = Torx size.
function torx_info(size) =
let(
info_arr = [
//T# OD ID H Re Ri
[ 1, [ 0.90, 0.65, 1.19, 0.059, 0.201]],
[ 2, [ 1.00, 0.73, 1.70, 0.069, 0.224]],
[ 3, [ 1.20, 0.87, 1.70, 0.081, 0.266]],
[ 4, [ 1.35, 0.98, 1.70, 0.090, 0.308]],
[ 5, [ 1.48, 1.08, 1.70, 0.109, 0.330]],
[ 6, [ 1.75, 1.27, 1.87, 0.132, 0.383]],
[ 7, [ 2.08, 1.50, 3.10, 0.161, 0.446]],
[ 8, [ 2.40, 1.75, 3.10, 0.190, 0.510]],
[ 9, [ 2.58, 1.87, 3.35, 0.207, 0.554]],
[ 10, [ 2.80, 2.05, 3.61, 0.229, 0.598]],
[ 15, [ 3.35, 2.40, 3.86, 0.267, 0.716]],
[ 20, [ 3.95, 2.85, 4.12, 0.305, 0.859]],
[ 25, [ 4.50, 3.25, 4.50, 0.375, 0.920]],
[ 27, [ 5.07, 3.65, 4.75, 0.390, 1.108]],
[ 30, [ 5.60, 4.05, 5.00, 0.451, 1.194]],
[ 40, [ 6.75, 4.85, 5.64, 0.546, 1.428]],
[ 45, [ 7.93, 5.64, 6.27, 0.574, 1.796]],
[ 50, [ 8.95, 6.45, 6.53, 0.775, 1.816]],
[ 55, [ 11.35, 8.05, 6.78, 0.867, 2.667]],
[ 60, [ 13.45, 9.60, 8.22, 1.067, 2.883]],
[ 70, [ 15.70, 11.20, 9.01, 1.194, 3.477]],
[ 80, [ 17.75, 12.80, 9.95, 1.526, 3.627]],
[ 90, [ 20.20, 14.40, 10.61, 1.530, 4.468]],
[100, [ 22.40, 16.00, 11.40, 1.720, 4.925]],
],
found = [for(info=info_arr) if(info[0]==size) info[1]]
)
assert(found, "Unsupported Torx size.")
found[0];
// Function: torx_diam() // Function: torx_diam()
// Usage: // Usage:
// diam = torx_diam(size); // diam = torx_diam(size);
// Description: Get the typical outer diameter of Torx profile. // Description: Get the typical outer diameter of Torx profile.
// Arguments: // Arguments:
// size = Torx size. // size = Torx size.
function torx_diam(size) = lookup(size, [ function torx_diam(size) = torx_info(size)[0];
[ 6, 1.75],
[ 8, 2.40],
[ 10, 2.80],
[ 15, 3.35],
[ 20, 3.95],
[ 25, 4.50],
[ 30, 5.60],
[ 40, 6.75],
[ 45, 7.93],
[ 50, 8.95],
[ 55, 11.35],
[ 60, 13.45],
[ 70, 15.70],
[ 80, 17.75],
[ 90, 20.20],
[100, 22.40]
]);
/*
[
[ 1, 0.90],
[ 2, 1.00],
[ 3, 1.20],
[ 4, 1.35],
[ 5, 1.50],
[ 6, 1.75],
[ 7, 2.10],
[ 8, 2.40],
[ 9, 2.60],
[ 10, 2.80],
[ 15, 3.35],
[ 20, 3.95],
[ 25, 4.50],
[ 27, 5.10],
[ 30, 5.60],
[ 35, 5.90],
[ 40, 6.75],
[ 45, 7.93],
[ 50, 8.95],
[ 55, 11.35],
[ 60, 13.45],
[ 70, 15.70],
[ 80, 17.75],
[ 90, 20.20],
[100, 22.40]
];
*/
/// Internal Function: torx_inner_diam()
/// Usage:
/// diam = torx_inner_diam(size);
/// Description: Get typical inner diameter of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_inner_diam(size) = lookup(size, [
[ 6, 1.27],
[ 8, 1.75],
[ 10, 2.05],
[ 15, 2.40],
[ 20, 2.85],
[ 25, 3.25],
[ 30, 4.05],
[ 40, 4.85],
[ 45, 5.64],
[ 50, 6.45],
[ 55, 8.05],
[ 60, 9.60],
[ 70, 11.20],
[ 80, 12.80],
[ 90, 14.40],
[100, 16.00]
]);
/*
[
[ 1, 0.60],
[ 2, 0.07],
[ 3, 0.85],
[ 4, 1.00],
[ 5, 1.10],
[ 6, 1.27],
[ 7, 1.50],
[ 8, 1.75],
[ 9, 1.90],
[ 10, 2.05],
[ 15, 2.40],
[ 20, 2.85],
[ 25, 3.25],
[ 27, 3.68],
[ 30, 4.05],
[ 40, 4.85],
[ 45, 5.64],
[ 50, 6.45],
[ 55, 8.05],
[ 60, 9.60],
[ 70, 11.20],
[ 80, 12.80],
[ 90, 14.40],
[100, 16.00]
]);
*/
// Function: torx_depth() // Function: torx_depth()
// Usage: // Usage:
// depth = torx_depth(size); // depth = torx_depth(size);
// Description: Gets typical drive hole depth. // Description: Gets typical drive hole depth.
// Arguments: // Arguments:
// size = Torx size. // size = Torx size.
function torx_depth(size) = lookup(size, [ function torx_depth(size) = torx_info(size)[2];
[ 6, 1.82],
[ 8, 3.05],
[ 10, 3.56],
[ 15, 3.81],
[ 20, 4.07],
[ 25, 4.45],
[ 30, 4.95],
[ 40, 5.59],
[ 45, 6.22],
[ 50, 6.48],
[ 55, 6.73],
[ 60, 8.17],
[ 70, 8.96],
[ 80, 9.90],
[ 90, 10.56],
[100, 11.35]
]);
/// Internal Function: torx_tip_radius()
/// Usage:
/// rad = torx_tip_radius(size);
/// Description: Gets minor rounding radius of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_tip_radius(size) = lookup(size, [
[ 6, 0.132],
[ 8, 0.190],
[ 10, 0.229],
[ 15, 0.267],
[ 20, 0.305],
[ 25, 0.375],
[ 30, 0.451],
[ 40, 0.546],
[ 45, 0.574],
[ 50, 0.775],
[ 55, 0.867],
[ 60, 1.067],
[ 70, 1.194],
[ 80, 1.526],
[ 90, 1.530],
[100, 1.720]
]);
/// Internal Function: torx_rounding_radius()
/// Usage:
/// rad = torx_rounding_radius(size);
/// Description: Gets major rounding radius of Torx profile.
/// Arguments:
/// size = Torx size.
function _torx_rounding_radius(size) = lookup(size, [
[ 6, 0.383],
[ 8, 0.510],
[ 10, 0.598],
[ 15, 0.716],
[ 20, 0.859],
[ 25, 0.920],
[ 30, 1.194],
[ 40, 1.428],
[ 45, 1.796],
[ 50, 1.816],
[ 55, 2.667],
[ 60, 2.883],
[ 70, 3.477],
[ 80, 3.627],
[ 90, 4.468],
[100, 4.925]
]);

View file

@ -52,6 +52,7 @@ use <builtins.scad>
// Example: Called as Function // Example: Called as Function
// vnf = cube([20,40,50]); // vnf = cube([20,40,50]);
// vnf_polyhedron(vnf); // vnf_polyhedron(vnf);
module cube(size=1, center, anchor, spin=0, orient=UP) module cube(size=1, center, anchor, spin=0, orient=UP)
{ {
anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]); anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]);
@ -177,6 +178,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
// cuboid([4,2,1], rounding=2, edges=[FWD+RIGHT,BACK+LEFT]); // cuboid([4,2,1], rounding=2, edges=[FWD+RIGHT,BACK+LEFT]);
// Example: Standard Connectors // Example: Standard Connectors
// cuboid(40) show_anchors(); // cuboid(40) show_anchors();
module cuboid( module cuboid(
size=[1,1,1], size=[1,1,1],
p1, p2, p1, p2,
@ -633,6 +635,7 @@ function cuboid(
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors // Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5])
// show_anchors(); // show_anchors();
module prismoid( module prismoid(
size1, size2, h, shift=[0,0], size1, size2, h, shift=[0,0],
rounding=0, rounding1, rounding2, rounding=0, rounding1, rounding2,
@ -775,6 +778,7 @@ function prismoid(
// octahedron(size=40); // octahedron(size=40);
// Example: Anchors // Example: Anchors
// octahedron(size=40) show_anchors(); // octahedron(size=40) show_anchors();
module octahedron(size=1, anchor=CENTER, spin=0, orient=UP) { module octahedron(size=1, anchor=CENTER, spin=0, orient=UP) {
vnf = octahedron(size=size); vnf = octahedron(size=size);
attachable(anchor,spin,orient, vnf=vnf, extent=true) { attachable(anchor,spin,orient, vnf=vnf, extent=true) {
@ -900,6 +904,7 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) =
// rounding1=[5,0,10,0], irounding1=[3,0,8,0], // rounding1=[5,0,10,0], irounding1=[3,0,8,0],
// rounding2=[0,5,0,10], irounding2=[0,3,0,8] // rounding2=[0,5,0,10], irounding2=[0,3,0,8]
// ); // );
module rect_tube( module rect_tube(
h, size, isize, center, shift=[0,0], h, size, isize, center, shift=[0,0],
wall, size1, size2, isize1, isize2, wall, size1, size2, isize1, isize2,
@ -1014,6 +1019,7 @@ function rect_tube(
// wedge([20, 40, 15]); // wedge([20, 40, 15]);
// Example: Standard Connectors // Example: Standard Connectors
// wedge([20, 40, 15]) show_anchors(); // wedge([20, 40, 15]) show_anchors();
module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP) module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP)
{ {
size = scalar_vec3(size); size = scalar_vec3(size);
@ -1095,6 +1101,7 @@ function wedge(size=[1,1,1], center, anchor, spin=0, orient=UP) =
// cylinder(h=30, d=25) show_anchors(); // cylinder(h=30, d=25) show_anchors();
// cylinder(h=30, d1=25, d2=10) show_anchors(); // cylinder(h=30, d1=25, d2=10) show_anchors();
// } // }
module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
{ {
anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); anchor = get_anchor(anchor, center, BOTTOM, BOTTOM);
@ -1128,13 +1135,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
// Module: cyl() // Function&Module: cyl()
//
// Description:
// Creates cylinders in various anchorings and orientations, with optional rounding and chamfers.
// You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`,
// or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than
// the cylinder's length.
// //
// Usage: Normal Cylinders // Usage: Normal Cylinders
// cyl(l|h, r, [center], [circum=], [realign=]) [ATTACHMENTS]; // cyl(l|h, r, [center], [circum=], [realign=]) [ATTACHMENTS];
@ -1154,6 +1155,14 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
// cyl(l|h, r|d, rounding2=, ...); // cyl(l|h, r|d, rounding2=, ...);
// cyl(l|h, r|d, rounding1=, rounding2=, ...); // cyl(l|h, r|d, rounding1=, rounding2=, ...);
// //
// Topics: Cylinders, Textures, Rounding, Chamfers
//
// Description:
// Creates cylinders in various anchorings and orientations, with optional rounding and chamfers.
// You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`,
// or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than
// the cylinder's length.
//
// Arguments: // Arguments:
// l / h = Length of cylinder along oriented axis. Default: 1 // l / h = Length of cylinder along oriented axis. Default: 1
// r = Radius of cylinder. Default: 1 // r = Radius of cylinder. Default: 1
@ -1165,6 +1174,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
// d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder. // d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder.
// d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder. // d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder.
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false` // circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end.
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none. // chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
// chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none. // chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none.
// chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none. // chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none.
@ -1176,10 +1186,20 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
// rounding1 = The radius of the rounding on the bottom end of the cylinder. // rounding1 = The radius of the rounding on the bottom end of the cylinder.
// rounding2 = The radius of the rounding on the top end of the cylinder. // rounding2 = The radius of the rounding on the top end of the cylinder.
// realign = If true, rotate the cylinder by half the angle of one face. // realign = If true, rotate the cylinder by half the angle of one face.
// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named textures are supported.
// tex_size = An optional 2D target size for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
// tex_counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height.
// tex_inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false`
// tex_rot = If true, rotates the texture 90º.
// tex_scale = Scaling multiplier for the texture depth.
// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8
// tex_style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge"
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// //
// See Also: texture(), rotate_sweep()
//
// Example: By Radius // Example: By Radius
// xdistribute(30) { // xdistribute(30) {
// cyl(l=40, r=10); // cyl(l=40, r=10);
@ -1230,6 +1250,156 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
// cyl(l=30, d1=25, d2=10) show_anchors(); // cyl(l=30, d1=25, d2=10) show_anchors();
// } // }
// //
// Example: Texturing with heightfield diamonds
// cyl(h=40, r=20, texture="diamonds", tex_size=[5,5]);
//
// Example: Texturing with heightfield pyramids
// cyl(h=40, r1=20, r2=15,
// texture="pyramids", tex_size=[5,5],
// tex_style="convex");
//
// Example: Texturing with heightfield truncated pyramids
// cyl(h=40, r1=20, r2=15, chamfer=5,
// texture="trunc_pyramids",
// tex_size=[5,5], tex_style="convex");
//
// Example: Texturing with VNF tile "vnf_dots"
// cyl(h=40, r1=20, r2=15, rounding=9,
// texture="vnf_dots", tex_size=[5,5],
// tex_samples=6);
//
// Example: Texturing with VNF tile "vnf_bricks"
// cyl(h=50, r1=25, r2=20, shift=[0,10], rounding1=-10,
// texture="vnf_bricks", tex_size=[10,10],
// tex_scale=0.5, tex_style="concave");
//
// Example: No Texture Taper
// cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_size=[5,1]);
//
// Example: Taper Texure at Extreme Ends
// cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_taper=0,
// tex_size=[5,1]);
//
// Example: Taper Texture over First and Last 10%
// cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_taper=10,
// tex_size=[5,1]);
function cyl(
h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2,
circum=false, realign=false,
from_end=false, shift=[0,0],
texture, tex_size=[5,5], tex_counts,
tex_inset=false, tex_rot=false,
tex_scale=1, tex_samples,
tex_taper, tex_style="min_edge",
anchor, spin=0, orient=UP
) =
let(
l = first_defined([l, h, length, height, 1]),
_r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1),
_r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1),
sides = segs(max(_r1,_r2)),
sc = circum? 1/cos(180/sides) : 1,
r1 = _r1 * sc,
r2 = _r2 * sc,
phi = atan2(l, r2-r1),
anchor = get_anchor(anchor,center,BOT,CENTER)
)
assert(is_finite(l), "l/h/length/height must be a finite number.")
assert(is_finite(r1), "r/r1/d/d1 must be a finite number.")
assert(is_finite(r2), "r2 or d2 must be a finite number.")
assert(is_vector(shift,2), "shift must be a 2D vector.")
let(
vnf = texture != undef? _textured_cylinder(
l=l, r1=r1, r2=r2,
texture=texture, tex_size=tex_size,
counts=tex_counts, tex_scale=tex_scale,
inset=tex_inset, rot=tex_rot,
style=tex_style, taper=tex_taper,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
samples=tex_samples
) :
!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])?
cylinder(h=l, r1=r1, r2=r2, center=true, $fn=sides) :
let(
vang = atan2(l, r1-r2)/2,
chang = default(chamfang, 45),
chang1 = 90-first_defined([chamfang1, chamfang, vang]),
chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]),
checks1 =
assert(is_finite(chang) && chang>0 && chang<90, "chamfang must be a number between 0 and 90 (exclusive) if given.")
assert(is_finite(chang1) && chang1>0 && chang1<90, "chamfang1 must be a number between 0 and 90 (exclusive) if given.")
assert(is_finite(chang2) && chang2>0 && chang2<90, "chamfang2 must be a number between 0 and 90 (exclusive) if given.")
undef,
chamf = default(chamfer, 0) * (from_end? 1 : tan(chang1)),
chamf1 = first_defined([chamfer1, chamfer, 0]) * (from_end? 1 : tan(chang1)),
chamf2 = first_defined([chamfer2, chamfer, 0]) * (from_end? 1 : tan(chang2)),
round = default(rounding, 0),
round1 = first_defined([rounding1, rounding, 0]),
round2 = first_defined([rounding2, rounding, 0]),
dy1 = abs(first_defined([chamf1, round1, 0])),
dy2 = abs(first_defined([chamf2, round2, 0])),
checks2 =
assert(is_finite(chamf), "chamfer must be a finite number if given.")
assert(is_finite(chamf1), "chamfer1 must be a finite number if given.")
assert(is_finite(chamf2), "chamfer2 must be a finite number if given.")
assert(is_finite(round), "rounding must be a finite number if given.")
assert(is_finite(round1), "rounding1 must be a finite number if given.")
assert(is_finite(round2), "rounding2 must be a finite number if given.")
assert(chamf <= r1, "chamfer is larger than the r1 radius of the cylinder.")
assert(chamf <= r2, "chamfer is larger than the r2 radius of the cylinder.")
assert(chamf1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.")
assert(chamf2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.")
assert(round <= r1, "rounding is larger than the r1 radius of the cylinder.")
assert(round <= r2, "rounding is larger than the r2 radius of the cylinder.")
assert(round1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.")
assert(round2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.")
assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.")
undef,
path = [
[0,-l/2],
if (is_finite(chamf1) && !approx(chamf1,0))
let(
p1 = [r1-chamf1/tan(chang1),-l/2],
p2 = lerp([r1,-l/2],[r2,l/2],abs(chamf1)/l)
) each [p1,p2]
else if (is_finite(round1) && !approx(round1,0))
each arc(r=abs(round1), corner=[[(round1>0?0:1e6),-l/2],[r1,-l/2],[r2,l/2]])
else [r1,-l/2],
if (is_finite(chamf2) && !approx(chamf2,0))
let(
p1 = lerp([r2,l/2],[r1,-l/2],abs(chamf2)/l),
p2 = [r2-chamf2/tan(chang2),l/2]
) each [p1,p2]
else if (is_finite(round2) && !approx(round2,0))
each arc(r=abs(round2), corner=[[r1,-l/2],[r2,l/2],[(round2>0?0:1e6),l/2]])
else [r2,l/2],
[0,l/2]
]
) rotate_sweep(path),
skmat = down(l/2) *
skew(sxz=shift.x/l, syz=shift.y/l) *
up(l/2) *
zrot(realign? 180/sides : 0),
ovnf = apply(skmat, vnf)
)
reorient(anchor,spin,orient, r1=r1, r2=r2, l=l, shift=shift, p=ovnf);
module cyl( module cyl(
h, r, center, h, r, center,
l, r1, r2, l, r1, r2,
@ -1237,7 +1407,12 @@ module cyl(
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false, circum=false, realign=false,
from_end=false, shift=[0,0],
texture, tex_size=[5,5], tex_counts,
tex_inset=false, tex_rot=false,
tex_scale=1, tex_samples,
tex_taper, tex_style="min_edge",
anchor, spin=0, orient=UP anchor, spin=0, orient=UP
) { ) {
l = first_defined([l, h, 1]); l = first_defined([l, h, 1]);
@ -1249,82 +1424,72 @@ module cyl(
r2 = _r2 * sc; r2 = _r2 * sc;
phi = atan2(l, r2-r1); phi = atan2(l, r2-r1);
anchor = get_anchor(anchor,center,BOT,CENTER); anchor = get_anchor(anchor,center,BOT,CENTER);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { skmat = down(l/2) * skew(sxz=shift.x/l, syz=shift.y/l) * up(l/2);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, shift=shift) {
multmatrix(skmat)
zrot(realign? 180/sides : 0) { zrot(realign? 180/sides : 0) {
if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) { if (texture != undef) {
_textured_cylinder(
l=l, r1=r1, r2=r2,
texture=texture, tex_size=tex_size,
counts=tex_counts, tex_scale=tex_scale,
inset=tex_inset, rot=tex_rot,
style=tex_style, taper=tex_taper,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
convexity=10, samples=tex_samples
);
} else if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) {
cylinder(h=l, r1=r1, r2=r2, center=true, $fn=sides); cylinder(h=l, r1=r1, r2=r2, center=true, $fn=sides);
} else { } else {
vang = atan2(l, r1-r2)/2; vang = atan2(l, r1-r2)/2;
chang1 = 90-first_defined([chamfang1, chamfang, vang]); chang1 = 90-first_defined([chamfang1, chamfang, vang,]);
chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]); chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]);
cham1 = u_mul(first_defined([chamfer1, chamfer]) , (from_end? 1 : tan(chang1))); chamf = default(chamfer, 0) * (from_end? 1 : tan(chang1));
cham2 = u_mul(first_defined([chamfer2, chamfer]) , (from_end? 1 : tan(chang2))); chamf1 = first_defined([chamfer1, chamfer, 0]) * (from_end? 1 : tan(chang1));
fil1 = first_defined([rounding1, rounding]); chamf2 = first_defined([chamfer2, chamfer, 0]) * (from_end? 1 : tan(chang2));
fil2 = first_defined([rounding2, rounding]); round = default(rounding, 0);
if (chamfer != undef) { round1 = first_defined([rounding1, rounding, 0]);
round2 = first_defined([rounding2, rounding, 0]);
dy1 = abs(first_defined([chamf1, round1, 0]));
dy2 = abs(first_defined([chamf2, round2, 0]));
checks = checks =
assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.") assert(chamf <= r1, "chamfer is larger than the r1 radius of the cylinder.")
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder."); assert(chamf <= r2, "chamfer is larger than the r2 radius of the cylinder.")
} assert(chamf1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.")
if (cham1 != undef) { assert(chamf2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.")
check = assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder."); assert(round <= r1, "rounding is larger than the r1 radius of the cylinder.")
} assert(round <= r2, "rounding is larger than the r2 radius of the cylinder.")
if (cham2 != undef) { assert(round1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.")
check = assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder."); assert(round2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.")
} assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.")
if (rounding != undef) { undef;
checks = path = [
assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder.") [0,-l/2],
assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder."); if (is_finite(chamf1) && !approx(chamf1,0))
}
if (fil1 != undef) {
check = assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.");
}
if (fil2 != undef) {
check = assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.");
}
dy1 = abs(first_defined([cham1, fil1, 0]));
dy2 = abs(first_defined([cham2, fil2, 0]));
check = assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.");
path = concat(
[[0,l/2]],
!is_undef(cham2)? (
let( let(
p1 = [r2-cham2/tan(chang2),l/2], p1 = [r1-chamf1/tan(chang1),-l/2],
p2 = lerp([r2,l/2],[r1,-l/2],abs(cham2)/l) p2 = lerp([r1,-l/2],[r2,l/2],abs(chamf1)/l)
) [p1,p2] ) each [p1,p2]
) : !is_undef(fil2)? ( else if (is_finite(round1) && !approx(round1,0))
each arc(r=abs(round1), corner=[[(round1>0?0:1e6),-l/2],[r1,-l/2],[r2,l/2]])
else [r1,-l/2],
if (is_finite(chamf2) && !approx(chamf2,0))
let( let(
cn = circle_2tangents(abs(fil2), [r2-fil2,l/2], [r2,l/2], [r1,-l/2]), p1 = lerp([r2,l/2],[r1,-l/2],abs(chamf2)/l),
ang = fil2<0? phi : phi-180, p2 = [r2-chamf2/tan(chang2),l/2]
steps = ceil(abs(ang)/360*segs(abs(fil2))), ) each [p1,p2]
step = ang/steps, else if (is_finite(round2) && !approx(round2,0))
pts = [for (i=[0:1:steps]) let(a=90+i*step) cn[0]+abs(fil2)*[cos(a),sin(a)]] each arc(r=abs(round2), corner=[[r1,-l/2],[r2,l/2],[(round2>0?0:1e6),l/2]])
) pts else [r2,l/2],
) : [[r2,l/2]], [0,l/2]
];
!is_undef(cham1)? ( rotate_extrude(convexity=2) polygon(path);
let(
p1 = lerp([r1,-l/2],[r2,l/2],abs(cham1)/l),
p2 = [r1-cham1/tan(chang1),-l/2]
) [p1,p2]
) : !is_undef(fil1)? (
let(
cn = circle_2tangents(abs(fil1), [r1-fil1,-l/2], [r1,-l/2], [r2,l/2]),
ang = fil1<0? 180-phi : -phi,
steps = ceil(abs(ang)/360*segs(abs(fil1))),
step = ang/steps,
pts = [for (i=[0:1:steps]) let(a=(fil1<0?180:0)+(phi-90)+i*step) cn[0]+abs(fil1)*[cos(a),sin(a)]]
) pts
) : [[r1,-l/2]],
[[0,-l/2]]
);
rotate_extrude(convexity=2) {
polygon(path);
}
} }
} }
children(); children();
@ -1378,6 +1543,7 @@ module cyl(
// xcyl(l=35, d=20); // xcyl(l=35, d=20);
// xcyl(l=35, d1=30, d2=10); // xcyl(l=35, d1=30, d2=10);
// } // }
module xcyl( module xcyl(
h, r, d, r1, r2, d1, d2, l, h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
@ -1448,6 +1614,7 @@ module xcyl(
// ycyl(l=35, d=20); // ycyl(l=35, d=20);
// ycyl(l=35, d1=30, d2=10); // ycyl(l=35, d1=30, d2=10);
// } // }
module ycyl( module ycyl(
h, r, d, r1, r2, d1, d2, l, h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
@ -1519,6 +1686,7 @@ module ycyl(
// zcyl(l=35, d=20); // zcyl(l=35, d=20);
// zcyl(l=35, d1=30, d2=10); // zcyl(l=35, d1=30, d2=10);
// } // }
module zcyl( module zcyl(
h, r, d, r1, r2, d1, d2, l, h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
@ -1596,6 +1764,7 @@ module zcyl(
// tube(h=30, or1=40, or2=30, ir1=20, ir2=30); // tube(h=30, or1=40, or2=30, ir1=20, ir2=30);
// Example: Standard Connectors // Example: Standard Connectors
// tube(h=30, or=40, wall=5) show_anchors(); // tube(h=30, or=40, wall=5) show_anchors();
module tube( module tube(
h, or, ir, center, h, or, ir, center,
od, id, wall, od, id, wall,
@ -1673,6 +1842,7 @@ module tube(
// Example: Generating a VNF // Example: Generating a VNF
// vnf = pie_slice(ang=150, l=20, r1=30, r2=50); // vnf = pie_slice(ang=150, l=20, r1=30, r2=50);
// vnf_polyhedron(vnf); // vnf_polyhedron(vnf);
module pie_slice( module pie_slice(
h, r, ang=30, center, h, r, ang=30, center,
r1, r2, d, d1, d2, l, r1, r2, d, d1, d2, l,
@ -1696,7 +1866,6 @@ module pie_slice(
} }
} }
function pie_slice( function pie_slice(
h, r, ang=30, center, h, r, ang=30, center,
r1, r2, d, d1, d2, l, r1, r2, d, d1, d2, l,
@ -1774,6 +1943,7 @@ function pie_slice(
// Example: Called as Function // Example: Called as Function
// vnf = sphere(d=100, style="icosa"); // vnf = sphere(d=100, style="icosa");
// vnf_polyhedron(vnf); // vnf_polyhedron(vnf);
module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) { module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
if (!circum && style=="orig" && is_num(r)) { if (!circum && style=="orig" && is_num(r)) {
@ -1789,7 +1959,6 @@ module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP
} }
} }
function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) = function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) =
spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient);
@ -1884,6 +2053,7 @@ function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=
// Example: The dual of "icosa" features hexagons and always 12 pentagons: // Example: The dual of "icosa" features hexagons and always 12 pentagons:
// color("green")spheroid(r=10.01, $fn=256); // color("green")spheroid(r=10.01, $fn=256);
// spheroid(r=10, style="icosa", circum=true, $fn=16); // spheroid(r=10, style="icosa", circum=true, $fn=16);
module spheroid(r, style="aligned", d, circum=false, dual=false, anchor=CENTER, spin=0, orient=UP) module spheroid(r, style="aligned", d, circum=false, dual=false, anchor=CENTER, spin=0, orient=UP)
{ {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
@ -2194,6 +2364,7 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
// vnf_polyhedron(torus(d_min=15, od=60), convexity=4); // vnf_polyhedron(torus(d_min=15, od=60), convexity=4);
// Example: Standard Connectors // Example: Standard Connectors
// torus(od=60, id=30) show_anchors(); // torus(od=60, id=30) show_anchors();
module torus( module torus(
r_maj, r_min, center, r_maj, r_min, center,
d_maj, d_min, d_maj, d_min,
@ -2314,6 +2485,7 @@ function torus(
// Example(Spin,VPD=150,Med): Named Conical Connectors // Example(Spin,VPD=150,Med): Named Conical Connectors
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16) // teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
// show_anchors(std=false); // show_anchors(std=false);
module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anchor=CENTER, spin=0, orient=UP) module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anchor=CENTER, spin=0, orient=UP)
{ {
r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1); r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1);
@ -2432,6 +2604,7 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anc
// } // }
// Example: Standard Connectors // Example: Standard Connectors
// onion(d=30, ang=30, cap_h=20) show_anchors(); // onion(d=30, ang=30, cap_h=20) show_anchors();
module onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) module onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP)
{ {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
@ -2527,6 +2700,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) =
// text3d("Foobar", h=2, anchor=CENTER); // text3d("Foobar", h=2, anchor=CENTER);
// text3d("Foobar", h=2, anchor=str("baseline",CENTER)); // text3d("Foobar", h=2, anchor=str("baseline",CENTER));
// text3d("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); // text3d("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT));
module text3d(text, h=1, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="em", script="latin", anchor="baseline[-1,0,-1]", spin=0, orient=UP) { module text3d(text, h=1, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="em", script="latin", anchor="baseline[-1,0,-1]", spin=0, orient=UP) {
no_children($children); no_children($children);
dummy1 = dummy1 =
@ -2721,6 +2895,7 @@ function _cut_interp(pathcut, path, data) =
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1]; // kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1];
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP); // path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP);
module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0) module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0)
{ {
no_children($children); no_children($children);
@ -2849,6 +3024,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
// position(BOT+FRONT) // position(BOT+FRONT)
// interior_fillet(l=50, r=10, spin=180, orient=RIGHT); // interior_fillet(l=50, r=10, spin=180, orient=RIGHT);
// } // }
module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, orient=UP) { module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, orient=UP) {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
steps = ceil(segs(r)*(180-ang)/360); steps = ceil(segs(r)*(180-ang)/360);
@ -2896,7 +3072,7 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0,
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep() // See Also: heightfield(), cylindrical_heightfield()
// Example: // Example:
// heightfield(size=[100,100], bottom=-20, data=[ // heightfield(size=[100,100], bottom=-20, data=[
// for (y=[-180:4:180]) [ // for (y=[-180:4:180]) [
@ -2923,6 +3099,7 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0,
// size=[100,100], bottom=-20, data=fn, // size=[100,100], bottom=-20, data=fn,
// xrange=[-180:2:180], yrange=[-180:2:180] // xrange=[-180:2:180], yrange=[-180:2:180]
// ); // );
module heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04:1], yrange=[-1:0.04:1], style="default", convexity=10, anchor=CENTER, spin=0, orient=UP) module heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04:1], yrange=[-1:0.04:1], style="default", convexity=10, anchor=CENTER, spin=0, orient=UP)
{ {
size = is_num(size)? [size,size] : point2d(size); size = is_num(size)? [size,size] : point2d(size);
@ -3033,7 +3210,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep() // See Also: heightfield(), cylindrical_heightfield()
// Example(VPD=400;VPR=[55,0,150]): // Example(VPD=400;VPR=[55,0,150]):
// cylindrical_heightfield(l=100, r=30, base=5, data=[ // cylindrical_heightfield(l=100, r=30, base=5, data=[
// for (y=[-180:4:180]) [ // for (y=[-180:4:180]) [
@ -3057,6 +3234,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04
// l=100, r=30, base=5, data=fn, // l=100, r=30, base=5, data=fn,
// xrange=[-180:2:180], yrange=[-180:2:180] // xrange=[-180:2:180], yrange=[-180:2:180]
// ); // );
function cylindrical_heightfield( function cylindrical_heightfield(
data, l, r, base=1, data, l, r, base=1,
transpose=false, aspect=1, transpose=false, aspect=1,
@ -3176,6 +3354,7 @@ module cylindrical_heightfield(
// Example(2D,Big): Metric vs Imperial // Example(2D,Big): Metric vs Imperial
// ruler(12,width=50,inch=true,labels=true,maxscale=0); // ruler(12,width=50,inch=true,labels=true,maxscale=0);
// fwd(50)ruler(300,width=50,labels=true); // fwd(50)ruler(300,width=50,labels=true);
module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3, maxscale, module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3, maxscale,
colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP) colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP)
{ {

1053
skin.scad

File diff suppressed because it is too large Load diff

View file

@ -137,7 +137,7 @@ test_affine3d_skew();
module test_affine3d_skew_xy() { module test_affine3d_skew_xy() {
for(ya = [-89:3:89]) { for(ya = [-89:3:89]) {
for(xa = [-89:3:89]) { for(xa = [-89:3:89]) {
assert(affine3d_skew_xy(xa=xa, ya=ya) == [[1,0,tan(xa),0],[0,1,tan(ya),0],[0,0,1,0],[0,0,0,1]]); assert(affine3d_skew_xy(xa=xa, ya=ya) == [[1,tan(xa),0,0],[tan(ya),1,0,0],[0,0,1,0],[0,0,0,1]]);
} }
} }
} }
@ -147,7 +147,7 @@ test_affine3d_skew_xy();
module test_affine3d_skew_xz() { module test_affine3d_skew_xz() {
for(za = [-89:3:89]) { for(za = [-89:3:89]) {
for(xa = [-89:3:89]) { for(xa = [-89:3:89]) {
assert(affine3d_skew_xz(xa=xa, za=za) == [[1,tan(xa),0,0],[0,1,0,0],[0,tan(za),1,0],[0,0,0,1]]); assert(affine3d_skew_xz(xa=xa, za=za) == [[1,0,tan(xa),0],[0,1,0,0],[tan(za),0,1,0],[0,0,0,1]]);
} }
} }
} }
@ -157,7 +157,7 @@ test_affine3d_skew_xz();
module test_affine3d_skew_yz() { module test_affine3d_skew_yz() {
for(za = [-89:3:89]) { for(za = [-89:3:89]) {
for(ya = [-89:3:89]) { for(ya = [-89:3:89]) {
assert(affine3d_skew_yz(ya=ya, za=za) == [[1,0,0,0],[tan(ya),1,0,0],[tan(za),0,1,0],[0,0,0,1]]); assert(affine3d_skew_yz(ya=ya, za=za) == [[1,0,0,0],[0,1,tan(ya),0],[0,tan(za),1,0],[0,0,0,1]]);
} }
} }
} }

View file

@ -53,8 +53,8 @@ test_arc();
module test_dashed_stroke() { module test_dashed_stroke() {
segs = dashed_stroke([[0,0],[10,0]], dashpat=[3,2], closed=false); segs = dashed_stroke([[0,0],[15,0]], dashpat=[3,2], closed=false);
assert_equal(segs,[[[0,0],[3,0]], [[5,0],[8,0]]]); assert_approx(segs,[[[0,0],[2.5,0]],[[4+1/6,0],[6+2/3,0]],[[8+1/3,0],[10+5/6,0]],[[12.5,0],[15,0]]]);
} }
test_dashed_stroke(); test_dashed_stroke();

View file

@ -13,49 +13,16 @@ module test_torx_diam() {
test_torx_diam(); test_torx_diam();
module test_torx_inner_diam() {
assert_approx(_torx_inner_diam(10), 2.05);
assert_approx(_torx_inner_diam(15), 2.40);
assert_approx(_torx_inner_diam(20), 2.85);
assert_approx(_torx_inner_diam(25), 3.25);
assert_approx(_torx_inner_diam(30), 4.05);
assert_approx(_torx_inner_diam(40), 4.85);
}
test_torx_inner_diam();
module test_torx_depth() { module test_torx_depth() {
assert_approx(torx_depth(10), 3.56); assert_approx(torx_depth(10), 3.61);
assert_approx(torx_depth(15), 3.81); assert_approx(torx_depth(15), 3.86);
assert_approx(torx_depth(20), 4.07); assert_approx(torx_depth(20), 4.12);
assert_approx(torx_depth(25), 4.45); assert_approx(torx_depth(25), 4.50);
assert_approx(torx_depth(30), 4.95); assert_approx(torx_depth(30), 5,00);
assert_approx(torx_depth(40), 5.59); assert_approx(torx_depth(40), 5.64);
} }
test_torx_depth(); test_torx_depth();
module test_torx_tip_radius() {
assert_approx(_torx_tip_radius(10), 0.229);
assert_approx(_torx_tip_radius(15), 0.267);
assert_approx(_torx_tip_radius(20), 0.305);
assert_approx(_torx_tip_radius(25), 0.375);
assert_approx(_torx_tip_radius(30), 0.451);
assert_approx(_torx_tip_radius(40), 0.546);
}
test_torx_tip_radius();
module test_torx_rounding_radius() {
assert_approx(_torx_rounding_radius(10), 0.598);
assert_approx(_torx_rounding_radius(15), 0.716);
assert_approx(_torx_rounding_radius(20), 0.859);
assert_approx(_torx_rounding_radius(25), 0.920);
assert_approx(_torx_rounding_radius(30), 1.194);
assert_approx(_torx_rounding_radius(40), 1.428);
}
test_torx_rounding_radius();
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -178,23 +178,28 @@ function vnf_vertex_array(
let( let(
d42=norm(pts[i4]-pts[i2]), d42=norm(pts[i4]-pts[i2]),
d13=norm(pts[i1]-pts[i3]), d13=norm(pts[i1]-pts[i3]),
shortedge = d42<d13+EPSILON ? [[i1,i4,i2],[i2,i4,i3]] shortedge = d42<d13+EPSILON
? [[i1,i4,i2],[i2,i4,i3]]
: [[i1,i3,i2],[i1,i4,i3]] : [[i1,i3,i2],[i1,i4,i3]]
) )
shortedge shortedge
: style=="convex"? : style=="convex"?
let( // Find normal for 3 of the points. Is the other point above or below? let( // Find normal for 3 of the points. Is the other point above or below?
n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]),
convexfaces = n==0 ? [[i1,i4,i3]] convexfaces = n==0
: n*pts[i4] > n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] ? [[i1,i4,i3]]
: n*pts[i4] > n*pts[i1]
? [[i1,i4,i2],[i2,i4,i3]]
: [[i1,i3,i2],[i1,i4,i3]] : [[i1,i3,i2],[i1,i4,i3]]
) )
convexfaces convexfaces
: style=="concave"? : style=="concave"?
let( // Find normal for 3 of the points. Is the other point above or below? let( // Find normal for 3 of the points. Is the other point above or below?
n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]),
concavefaces = n==0 ? [[i1,i4,i3]] concavefaces = n==0
: n*pts[i4] <= n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] ? [[i1,i4,i3]]
: n*pts[i4] <= n*pts[i1]
? [[i1,i4,i2],[i2,i4,i3]]
: [[i1,i3,i2],[i1,i4,i3]] : [[i1,i3,i2],[i1,i4,i3]]
) )
concavefaces concavefaces
@ -573,10 +578,14 @@ function _bridge(pt, outer,eps) =
// vnf_wireframe(vnf,width=.25); // vnf_wireframe(vnf,width=.25);
function vnf_from_region(region, transform, reverse=false) = function vnf_from_region(region, transform, reverse=false) =
let ( let (
region = [for (path = region) deduplicate(path, closed=true)],
regions = region_parts(force_region(region)), regions = region_parts(force_region(region)),
vnfs = vnfs =
[ for (rgn = regions) [
let( cleaved = path3d(_cleave_connected_region(rgn)) ) for (rgn = regions)
let(
cleaved = path3d(_cleave_connected_region(rgn))
)
assert( cleaved, "The region is invalid") assert( cleaved, "The region is invalid")
let( let(
face = is_undef(transform)? cleaved : apply(transform,cleaved), face = is_undef(transform)? cleaved : apply(transform,cleaved),
@ -877,6 +886,11 @@ function _slice_3dpolygons(polys, dir, cuts) =
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// atype = Select "hull" or "intersect" anchor type. Default: "hull" // atype = Select "hull" or "intersect" anchor type. Default: "hull"
// Anchor Types:
// "hull" = Anchors to the virtual convex hull of the shape.
// "intersect" = Anchors to the surface of the shape.
// Extra Anchors:
// "origin" = Anchor at the origin, oriented UP.
module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") { module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") {
vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf; vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf;
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
@ -1226,7 +1240,6 @@ function vnf_bend(vnf,r,d,axis="Z") =
axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] : axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] :
axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] : axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] :
[p.y*sin(a), p.y*cos(a), p.z]] [p.y*sin(a), p.y*cos(a), p.z]]
) [new_vert,sliced[1]]; ) [new_vert,sliced[1]];
@ -1690,9 +1703,9 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false, opacity=0.
color(clr) { color(clr) {
if (is_vector(pts[0])) { if (is_vector(pts[0])) {
if (len(pts)==2) { if (len(pts)==2) {
stroke(pts, width=size, closed=true, endcaps="butt", hull=false, $fn=8); stroke(pts, width=size, closed=true, endcaps="butt", $fn=8);
} else if (len(pts)>2) { } else if (len(pts)>2) {
stroke(pts, width=size, closed=true, hull=false, $fn=8); stroke(pts, width=size, closed=true, $fn=8);
polyhedron(pts,[[for (i=idx(pts)) i]]); polyhedron(pts,[[for (i=idx(pts)) i]]);
} else { } else {
move_copies(pts) sphere(d=size*3, $fn=18); move_copies(pts) sphere(d=size*3, $fn=18);