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,10 +519,10 @@ function affine3d_skew_xy(xa=0, ya=0) =
assert(is_finite(xa))
assert(is_finite(ya))
[
[1, 0, tan(xa), 0],
[0, 1, tan(ya), 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
[ 1, tan(xa), 0, 0],
[tan(ya), 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1]
];
@ -551,10 +551,10 @@ function affine3d_skew_xz(xa=0, za=0) =
assert(is_finite(xa))
assert(is_finite(za))
[
[1, tan(xa), 0, 0],
[0, 1, 0, 0],
[0, tan(za), 1, 0],
[0, 0, 0, 1]
[ 1, 0, tan(xa), 0],
[ 0, 1, 0, 0],
[tan(za), 0, 1, 0],
[ 0, 0, 0, 1]
];
@ -583,10 +583,10 @@ function affine3d_skew_yz(ya=0, za=0) =
assert(is_finite(ya))
assert(is_finite(za))
[
[ 1, 0, 0, 0],
[tan(ya), 1, 0, 0],
[tan(za), 0, 1, 0],
[ 0, 0, 0, 1]
[1, 0, 0, 0],
[0, 1, tan(ya), 0],
[0, tan(za), 1, 0],
[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])
// [for(j=[0:3]) pts1[i]+pts2[j] ] ];
// 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);
// normals = bezier_patch_normals(patch, uv, 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() {
union() {
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") {
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 {
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() {
union() {
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") {
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 {
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
// seems wrong so this does specified thickness+texture
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") {
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 {
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=`.
// 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.
// 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
// path = [[0,100], [100,100], [200,0], [100,-100], [100,0]];
// stroke(path, width=20);
@ -157,7 +156,7 @@ function stroke(
endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle,
endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color,
trim, trim1, trim2,
convexity=10, hull=true
convexity=10
) = no_function("stroke");
@ -170,7 +169,7 @@ module stroke(
endcap_angle, endcap_angle1, endcap_angle2, joint_angle, dots_angle,
endcap_color, endcap_color1, endcap_color2, joint_color, dots_color, color,
trim, trim1, trim2,
convexity=10, hull=true
convexity=10
) {
no_children($children);
module setcolor(clr) {
@ -359,7 +358,7 @@ module stroke(
for (i = [1:1:len(path2)-2]) {
$fn = quantup(segs(widths[i]/2),4);
translate(path2[i]) {
if (joints != undef) {
if (joints != undef && joints != "round") {
joint_shape = _shape_path(
joints, width[i],
joint_width,
@ -372,18 +371,21 @@ module stroke(
? rot(from=BACK,to=v1)
: zrot(joint_angle);
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 {
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]);
v1 = path2[i] - path2[i-1];
v2 = path2[i+1] - path2[i];
ang = modang(v_theta(v2) - v_theta(v1));
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]) {
$fn = sides[i];
translate(path2[i]) {
if (joints != undef) {
if (joints != undef && joints != "round") {
joint_shape = _shape_path(
joints, width[i],
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 {
multmatrix(rotmats[i]) {
sphere(d=widths[i],style="aligned");
corner = select(path2,i-1,i+1);
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,41 +551,57 @@ module stroke(
// ---
// width = The width of the dashed line to draw. Module only. Default: 1
// 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
// path = [for (a=[-180:10:180]) [a/3,20*sin(a)]];
// dashed_stroke(path, [3,2], width=1);
// Example(2D): Closed Polygon
// path = circle(d=100,$fn=72);
// dashpat = [10,2,3,2,3,2];
// dashpat = [10,2, 3,2, 3,2];
// dashed_stroke(path, dashpat, width=1, closed=true);
// Example(FlatSpin,VPD=250): 3D Dashed Path
// path = [for (a=[-180:5:180]) [a/3, 20*cos(3*a), 20*sin(3*a)]];
// dashed_stroke(path, [3,2], width=1);
function dashed_stroke(path, dashpat=[3,3], closed=false) =
is_region(path) ? [for(p=path) each dashed_stroke(p,dashpat,closed=true)] :
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, fit=fit)
] :
let(
path = closed? close_path(path) : path,
dashpat = len(dashpat)%2==0? dashpat : concat(dashpat,[0]),
plen = path_length(path),
dlen = sum(dashpat),
doff = cumsum(dashpat),
reps = floor(plen / dlen),
step = plen / reps,
freps = plen / dlen,
reps = max(1, fit? round(freps) : floor(freps)),
tlen = !fit? plen :
reps * dlen + (closed? 0 : dashpat[0]),
sc = plen / tlen,
cuts = [
for (i=[0:1:reps-1], off=doff)
let (st=i*step, x=st+off)
if (x>0 && x<plen) x
for (i = [0:1:reps], off = doff*sc)
let (x = i*dlen*sc + off)
if (x > 0 && x < plen) x
],
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;
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);
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)
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]) {
// crn = select(path, i*2-1, i*2+1);
// 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) =
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);
// }
// Example: Masking by Attachment
// diff("mask")
// diff()
// cube(50, center=true) {
// edge_mask(TOP+RIGHT)
// #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);
// }
// Example: Masking by Attachment
// diff("mask")
// diff()
// cuboid(100, chamfer=20, trimcorners=false) {
// corner_mask(TOP+FWD+RIGHT)
// 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);
// }
// Example: Masking by Attachment
// diff("mask")
// diff()
// cube(100, center=true)
// edge_mask(FRONT+RIGHT)
// #rounding_edge_mask(l=$parent_size.z+0.01, r=25);
// Example: Multiple Masking by Attachment
// diff("mask")
// diff()
// cube([80,90,100], center=true) {
// let(p = $parent_size*1.01) {
// 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);
// }
// Example: Masking by Attachment
// diff("mask")
// diff()
// cube(size=[50, 60, 70]) {
// corner_mask(TOP)
// #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);
// }
// Example: Masking by Attachment
// diff("mask")
// diff()
// cyl(h=30, d=30) {
// 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");
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]):
// teardrop_edge_mask(l=20, r=10, angle=40);
// Example(VPD=300,VPR=[75,0,25]):
// diff("mask")
// diff()
// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
// edge_mask(BOT)
// 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:
// teardrop_corner_mask(r=20, angle=40);
// Example:
// diff("mask")
// diff()
// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
// edge_profile(BOT)
// mask2d_teardrop(r=10, angle=40);

View file

@ -5,140 +5,70 @@
// include <BOSL2/std.scad>
// include <BOSL2/nema_steppers.scad>
// 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
// Module: nema11_stepper()
// Description: Creates a model of a NEMA 11 stepper motor.
// Module: nema_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:
// size = The NEMA standard size of the stepper motor.
// 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
// 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`
// 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:
// nema11_stepper();
module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP)
// Anchor Types:
// "shaft" = Anchor relative to the shaft.
// "plinth" = Anchor relative to the plinth.
// "body" = Anchor relative to the motor body.
// "screws" = Anchor relative to the screw hole centers. ie: TOP+RIGHT+FRONT is the center-top of the front-right screwhole.
// See Also: nema_stepper_motor(), nema_mount_mask()
// Examples:
// nema_stepper_motor(size=8, h=24, shaft_len=15);
// nema_stepper_motor(size=11, h=24, shaft_len=20);
// nema_stepper_motor(size=17, h=40, shaft_len=30);
// nema_stepper_motor(size=23, h=50, shaft_len=40);
// 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;
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() {
info = nema_motor_info(size);
motor_width = info[0];
plinth_height = info[1];
plinth_diam = info[2];
screw_spacing = info[3];
screw_size = info[4];
screw_depth = info[5];
shaft_diam = info[6];
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]) :
atype=="plinth"? attach_geom(r=plinth_diam/2, h=plinth_height, cp=[0,0,h/2+plinth_height/2]) :
atype=="body"? attach_geom(size=[motor_width, motor_width, h]) :
atype=="screws"? attach_geom(size=[screw_spacing, screw_spacing, screw_depth], cp=[0,0,h/2-screw_depth/2]) :
assert(in_list(atype, ["shaft", "plinth", "body", "screws"]));
attachable(anchor,spin,orient, geom=geom) {
up(h/2) {
if (details == false) {
slop = get_slop();
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width+2*slop, motor_width+2*slop, h+slop], anchor=TOP);
color([0.6, 0.6, 0.6])
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() {
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")
xcopies(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]) {
difference() {
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: 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() {
color("silver") cylinder(h=shaft_len, d=shaft_diam, $fn=max(12,segs(shaft_diam/2)));
} else {
difference() {
union() {
color([0.4, 0.4, 0.4])
cuboid(size=[motor_width, motor_width, h], chamfer=2, 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)));
}
cuboid([motor_width, motor_width, h], rounding=screw_size, edges="Z", anchor=TOP);
color([0.6, 0.6, 0.6]) {
difference() {
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")
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]) {
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)));
cylinder(h=shaft_len, d=shaft_diam, $fn=max(12,segs(shaft_diam/2)));
}
color([0.4, 0.4, 0.4]) {
xcopies(screw_spacing) {
ycopies(screw_spacing) {
cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2)));
down(screw_depth) cuboid([screw_inset, screw_inset, h], 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();
@ -444,63 +115,59 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient
// Section: Masking Modules
// Module: nema_mount_holes()
// Module: nema_mount_mask()
// Usage:
// nema_mount_mask(size, depth, l, ...);
// Topics: Parts, Motors
// Description: Creates a mask to use when making standard NEMA stepper motor mounts.
// Arguments:
// size = The standard NEMA motor size to make a mount for.
// depth = The thickness of the mounting hole mask. Default: 5
// l = The length of the slots, for making an adjustable motor mount. Default: 5
// ---
// 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:
// nema_mount_holes(size=14, depth=5, l=5);
// Example:
// nema_mount_holes(size=17, depth=5, l=5);
// Example:
// nema_mount_holes(size=17, depth=5, l=0);
module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
// Anchor Types:
// "full" = Anchor relative the full mask.
// "screws" = Anchor relative to the screw hole centers. ie: TOP+RIGHT+FRONT is the center-top of the front-right screwhole.
// See Also: nema_stepper_motor(), nema_mount_mask()
// Examples:
// nema_mount_mask(size=14, depth=5, l=5);
// nema_mount_mask(size=17, depth=5, l=5);
// nema_mount_mask(size=17, depth=5, l=0);
module nema_mount_mask(size, depth=5, l=5, atype="full", anchor=CENTER, spin=0, orient=UP)
{
motor_width = nema_motor_width(size);
plinth_diam = nema_motor_plinth_diam(size)+get_slop();
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size)+get_slop();
anchors = [
named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]),
named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]),
named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]),
named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]),
];
slop = get_slop();
info = nema_motor_info(size);
motor_width = info[0];
plinth_height = info[1];
plinth_diam = info[2] + slop;
screw_spacing = info[3];
screw_size = info[4] + slop;
screw_depth = info[5];
shaft_diam = info[6];
screwfn = quantup(max(8,segs(screw_size/2)),4);
plinthfn = quantup(max(8,segs(plinth_diam/2)),4);
s = [screw_spacing+screw_size, screw_spacing+screw_size+l, depth];
attachable(anchor,spin,orient, size=s, anchors=anchors) {
s = atype=="full"? [screw_spacing+screw_size, screw_spacing+screw_size+l, depth] :
atype=="screws"? [screw_spacing, screw_spacing, depth] :
assert(in_list(atype, ["full", "screws"]));
attachable(anchor,spin,orient, size=s) {
union() {
xcopies(screw_spacing) {
ycopies(screw_spacing) {
if (l>0) {
union() {
if (l > 0) {
ycopies(l) cyl(h=depth, d=screw_size, $fn=screwfn);
cube([screw_size, l, depth], center=true);
}
} else {
cyl(h=depth, d=screw_size, $fn=screwfn);
}
}
}
if (l>0) {
union () {
if (l > 0) {
ycopies(l) cyl(h=depth, d=plinth_diam, $fn=plinthfn);
cube([plinth_diam, l, depth], center=true);
}
} else {
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()
// Description: Creates a mask to use when making NEMA 11 stepper motor mounts.
// Section: Functions
// 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:
// 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:
// nema11_mount_holes(depth=5, l=5);
// Example:
// nema11_mount_holes(depth=5, l=0);
module nema11_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP)
{
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();
}
// size = The standard NEMA motor size.
function nema_motor_info(size) =
let(
info_arr = [
[ 6, [ 14.0, 1.50, 11.0, 11.50, 1.6, 2.5, 4.00]],
[ 8, [ 20.3, 1.50, 16.0, 15.40, 2.0, 2.5, 4.00]],
[11, [ 28.2, 1.50, 22.0, 23.11, 2.6, 3.0, 5.00]],
[14, [ 35.2, 2.00, 22.0, 26.00, 3.0, 4.5, 5.00]],
[17, [ 42.3, 2.00, 22.0, 31.00, 3.0, 4.5, 5.00]],
[23, [ 57.0, 1.60, 38.1, 47.00, 5.1, 4.8, 6.35]],
[34, [ 86.0, 2.00, 73.0, 69.60, 6.5, 10.0, 14.00]],
[42, [110.0, 1.50, 55.5, 88.90, 8.5, 12.7, 19.00]],
],
found = [for(info=info_arr) if(info[0]==size) info[1]]
)
assert(found, "Unsupported NEMA size.")
found[0];

View file

@ -143,8 +143,8 @@ function phillips_diam(size, depth) =
// Examples:
// torx_mask(size=30, l=10, $fa=1, $fs=1);
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_diam(size);
anchor = get_anchor(anchor, center, BOT, BOT);
attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) {
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);
module torx_mask2d(size) {
no_children($children);
od = torx_diam(size);
id = _torx_inner_diam(size);
tip = _torx_tip_radius(size);
rounding = _torx_rounding_radius(size);
info = torx_info(size);
od = info[0];
id = info[1];
tip = info[3];
rounding = info[4];
base = od - 2*tip;
$fn = quantup(segs(od/2),12);
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()
// Usage:
// diam = torx_diam(size);
// Description: Get the typical outer diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_diam(size) = lookup(size, [
[ 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]
]);
function torx_diam(size) = torx_info(size)[0];
/*
[
[ 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()
// Usage:
// depth = torx_depth(size);
// Description: Gets typical drive hole depth.
// Arguments:
// size = Torx size.
function torx_depth(size) = lookup(size, [
[ 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]
]);
function torx_depth(size) = torx_info(size)[2];

View file

@ -52,6 +52,7 @@ use <builtins.scad>
// Example: Called as Function
// vnf = cube([20,40,50]);
// vnf_polyhedron(vnf);
module cube(size=1, center, anchor, spin=0, orient=UP)
{
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]);
// Example: Standard Connectors
// cuboid(40) show_anchors();
module cuboid(
size=[1,1,1],
p1, p2,
@ -633,6 +635,7 @@ function cuboid(
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5])
// show_anchors();
module prismoid(
size1, size2, h, shift=[0,0],
rounding=0, rounding1, rounding2,
@ -775,6 +778,7 @@ function prismoid(
// octahedron(size=40);
// Example: Anchors
// octahedron(size=40) show_anchors();
module octahedron(size=1, anchor=CENTER, spin=0, orient=UP) {
vnf = octahedron(size=size);
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],
// rounding2=[0,5,0,10], irounding2=[0,3,0,8]
// );
module rect_tube(
h, size, isize, center, shift=[0,0],
wall, size1, size2, isize1, isize2,
@ -1014,6 +1019,7 @@ function rect_tube(
// wedge([20, 40, 15]);
// Example: Standard Connectors
// wedge([20, 40, 15]) show_anchors();
module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP)
{
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, d1=25, d2=10) show_anchors();
// }
module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
{
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()
//
// 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.
// Function&Module: cyl()
//
// Usage: Normal Cylinders
// 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, 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:
// l / h = Length of cylinder along oriented axis. 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.
// 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`
// 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.
// 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.
@ -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.
// 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.
// 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`
// 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`
//
// See Also: texture(), rotate_sweep()
//
// Example: By Radius
// xdistribute(30) {
// 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();
// }
//
// 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(
h, r, center,
l, r1, r2,
@ -1237,7 +1407,12 @@ module cyl(
chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2,
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
) {
l = first_defined([l, h, 1]);
@ -1245,86 +1420,76 @@ module cyl(
_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;
r1 = _r1 * sc;
r2 = _r2 * sc;
phi = atan2(l, r2-r1);
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) {
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);
} else {
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]);
cham1 = u_mul(first_defined([chamfer1, chamfer]) , (from_end? 1 : tan(chang1)));
cham2 = u_mul(first_defined([chamfer2, chamfer]) , (from_end? 1 : tan(chang2)));
fil1 = first_defined([rounding1, rounding]);
fil2 = first_defined([rounding2, rounding]);
if (chamfer != 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]));
checks =
assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.")
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
}
if (cham1 != undef) {
check = assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
}
if (cham2 != undef) {
check = assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
}
if (rounding != undef) {
checks =
assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder.")
assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder.");
}
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)? (
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 = [r2-cham2/tan(chang2),l/2],
p2 = lerp([r2,l/2],[r1,-l/2],abs(cham2)/l)
) [p1,p2]
) : !is_undef(fil2)? (
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(
cn = circle_2tangents(abs(fil2), [r2-fil2,l/2], [r2,l/2], [r1,-l/2]),
ang = fil2<0? phi : phi-180,
steps = ceil(abs(ang)/360*segs(abs(fil2))),
step = ang/steps,
pts = [for (i=[0:1:steps]) let(a=90+i*step) cn[0]+abs(fil2)*[cos(a),sin(a)]]
) pts
) : [[r2,l/2]],
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]
];
!is_undef(cham1)? (
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);
}
rotate_extrude(convexity=2) polygon(path);
}
}
children();
@ -1378,6 +1543,7 @@ module cyl(
// xcyl(l=35, d=20);
// xcyl(l=35, d1=30, d2=10);
// }
module xcyl(
h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2,
@ -1448,6 +1614,7 @@ module xcyl(
// ycyl(l=35, d=20);
// ycyl(l=35, d1=30, d2=10);
// }
module ycyl(
h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2,
@ -1519,6 +1686,7 @@ module ycyl(
// zcyl(l=35, d=20);
// zcyl(l=35, d1=30, d2=10);
// }
module zcyl(
h, r, d, r1, r2, d1, d2, l,
chamfer, chamfer1, chamfer2,
@ -1596,6 +1764,7 @@ module zcyl(
// tube(h=30, or1=40, or2=30, ir1=20, ir2=30);
// Example: Standard Connectors
// tube(h=30, or=40, wall=5) show_anchors();
module tube(
h, or, ir, center,
od, id, wall,
@ -1673,6 +1842,7 @@ module tube(
// Example: Generating a VNF
// vnf = pie_slice(ang=150, l=20, r1=30, r2=50);
// vnf_polyhedron(vnf);
module pie_slice(
h, r, ang=30, center,
r1, r2, d, d1, d2, l,
@ -1696,7 +1866,6 @@ module pie_slice(
}
}
function pie_slice(
h, r, ang=30, center,
r1, r2, d, d1, d2, l,
@ -1774,6 +1943,7 @@ function pie_slice(
// Example: Called as Function
// vnf = sphere(d=100, style="icosa");
// vnf_polyhedron(vnf);
module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) {
r = get_radius(r=r, d=d, dflt=1);
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) =
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:
// color("green")spheroid(r=10.01, $fn=256);
// 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)
{
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);
// Example: Standard Connectors
// torus(od=60, id=30) show_anchors();
module torus(
r_maj, r_min, center,
d_maj, d_min,
@ -2314,6 +2485,7 @@ function torus(
// Example(Spin,VPD=150,Med): Named Conical Connectors
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
// 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)
{
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
// onion(d=30, ang=30, cap_h=20) show_anchors();
module onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP)
{
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=str("baseline",CENTER));
// 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) {
no_children($children);
dummy1 =
@ -2721,6 +2895,7 @@ function _cut_interp(pathcut, path, data) =
// color("red")stroke(path, width=.3);
// 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);
module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0)
{
no_children($children);
@ -2849,6 +3024,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
// position(BOT+FRONT)
// 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) {
r = get_radius(r=r, d=d, dflt=1);
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`
// 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`
// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep()
// See Also: heightfield(), cylindrical_heightfield()
// Example:
// heightfield(size=[100,100], bottom=-20, data=[
// 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,
// 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)
{
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`
// 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`
// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep()
// See Also: heightfield(), cylindrical_heightfield()
// Example(VPD=400;VPR=[55,0,150]):
// cylindrical_heightfield(l=100, r=30, base=5, data=[
// 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,
// xrange=[-180:2:180], yrange=[-180:2:180]
// );
function cylindrical_heightfield(
data, l, r, base=1,
transpose=false, aspect=1,
@ -3176,6 +3354,7 @@ module cylindrical_heightfield(
// Example(2D,Big): Metric vs Imperial
// ruler(12,width=50,inch=true,labels=true,maxscale=0);
// fwd(50)ruler(300,width=50,labels=true);
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)
{

1057
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() {
for(ya = [-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() {
for(za = [-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() {
for(za = [-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() {
segs = dashed_stroke([[0,0],[10,0]], dashpat=[3,2], closed=false);
assert_equal(segs,[[[0,0],[3,0]], [[5,0],[8,0]]]);
segs = dashed_stroke([[0,0],[15,0]], dashpat=[3,2], closed=false);
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();

View file

@ -13,49 +13,16 @@ module 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() {
assert_approx(torx_depth(10), 3.56);
assert_approx(torx_depth(15), 3.81);
assert_approx(torx_depth(20), 4.07);
assert_approx(torx_depth(25), 4.45);
assert_approx(torx_depth(30), 4.95);
assert_approx(torx_depth(40), 5.59);
assert_approx(torx_depth(10), 3.61);
assert_approx(torx_depth(15), 3.86);
assert_approx(torx_depth(20), 4.12);
assert_approx(torx_depth(25), 4.50);
assert_approx(torx_depth(30), 5,00);
assert_approx(torx_depth(40), 5.64);
}
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

View file

@ -178,23 +178,28 @@ function vnf_vertex_array(
let(
d42=norm(pts[i4]-pts[i2]),
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]]
)
shortedge
: style=="convex"?
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]),
convexfaces = n==0 ? [[i1,i4,i3]]
: n*pts[i4] > n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]]
convexfaces = n==0
? [[i1,i4,i3]]
: n*pts[i4] > n*pts[i1]
? [[i1,i4,i2],[i2,i4,i3]]
: [[i1,i3,i2],[i1,i4,i3]]
)
convexfaces
: style=="concave"?
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]),
concavefaces = n==0 ? [[i1,i4,i3]]
: n*pts[i4] <= n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]]
concavefaces = n==0
? [[i1,i4,i3]]
: n*pts[i4] <= n*pts[i1]
? [[i1,i4,i2],[i2,i4,i3]]
: [[i1,i3,i2],[i1,i4,i3]]
)
concavefaces
@ -573,10 +578,14 @@ function _bridge(pt, outer,eps) =
// vnf_wireframe(vnf,width=.25);
function vnf_from_region(region, transform, reverse=false) =
let (
region = [for (path = region) deduplicate(path, closed=true)],
regions = region_parts(force_region(region)),
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")
let(
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`
// 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"
// 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") {
vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf;
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=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] :
[p.y*sin(a), p.y*cos(a), p.z]]
) [new_vert,sliced[1]];
@ -1690,9 +1703,9 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false, opacity=0.
color(clr) {
if (is_vector(pts[0])) {
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) {
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]]);
} else {
move_copies(pts) sphere(d=size*3, $fn=18);