diff --git a/gears.scad b/gears.scad index 370c44d..658a039 100644 --- a/gears.scad +++ b/gears.scad @@ -118,9 +118,12 @@ function _inherit_gear_thickness(thickness,dflt=10) = // right(8.3) back(74) zrot(87-360/30) zrot(10,cp=[pitchpt,0]) stroke(arc(angle=[0,20],r=10.5),endcaps="arrow2",width=.25); // back(84) right(13) text("pressure angle",size=2.5); // } +// stroke(arc(r=pitch_radius(mod=5,teeth=30),angle=[87,87+12]),width=.4,endcaps="arrow2",color="red"); +// color([1,0,0,1]) back(70)right(-13)zrot(4)text("circular pitch", size=2.5); // Continues: -// The size of the teeth can be specified as the circular pitch, the distance along the pitch circle -// from the start of one tooth to the start of the text tooth. The circular pitch can be computed as +// The size of the teeth can be specified as the *circular pitch*, which is the tooth width, or more precisely, +// the distance along the pitch circle from the start of one tooth to the start of the text tooth. +// The circular pitch can be computed as // `PI*d/teeth` where `d` is the diameter of the pitch circle and `teeth` is the number of teeth on the gear. // This simply divides up the pitch circle into the specified number of teeth. However, the customary // way to specify metric gears is using the module, ratio of the diameter of the gear to the number of teeth: `m=d/teeth`. diff --git a/masks3d.scad b/masks3d.scad index 622a4a1..fcaf576 100644 --- a/masks3d.scad +++ b/masks3d.scad @@ -173,13 +173,14 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // Section: Rounding Masks // Module: rounding_edge_mask() +// Aliases: fillet() // Synopsis: Creates a shape to round a 90° edge. // SynTags: Geom // Topics: Masks, Rounding, Shapes (3D) // See Also: rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask(), default_tag(), diff() // Usage: -// rounding_edge_mask(l|h=|length=|height=, r|d=, [excess=]) [ATTACHMENTS]; -// rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [excess=]) [ATTACHMENTS]; +// rounding_edge_mask(l|h=|length=|height=, r|d=, [ang], [excess=]) [ATTACHMENTS]; +// rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=]) [ATTACHMENTS]; // Description: // Creates a shape that can be used to round a vertical 90° edge. // Difference it from the object to be rounded. The center of the mask @@ -187,6 +188,7 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // Arguments: // l/h/length/height = Length of mask. // r = Radius of the rounding. +// ang = Angle between faces for rounding. Default: 90 // --- // r1 = Bottom radius of rounding. // r2 = Top radius of rounding. @@ -200,16 +202,37 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // Side Effects: // Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. // Example(VPD=200,VPR=[55,0,120]): +// rounding_edge_mask(l=50, r=15); +// Example(VPD=200,VPR=[55,0,120]): With different radii at each end // rounding_edge_mask(l=50, r1=10, r2=25); -// Example: +// Example(VPD=200,VPR=[55,0,120]): Acute angle +// rounding_edge_mask(l=50, r=10, ang=45); +// Example(VPD=200,VPR=[55,0,120]): A large excess +// rounding_edge_mask(l=50, r=15,excess=4); +// Example: Subtracting from a cube // difference() { // cube(size=100, center=false); -// #rounding_edge_mask(l=100, r=25, orient=UP, anchor=BOTTOM); +// #rounding_edge_mask(l=100, r=25, anchor=BOTTOM); // } // Example: Varying Rounding Radius // difference() { // cube(size=50, center=false); -// #rounding_edge_mask(l=50, r1=25, r2=10, orient=UP, anchor=BOTTOM); +// down(1)rounding_edge_mask(l=52, r1=25, r2=10, anchor=BOTTOM); +// } +// Example: Angle not 90 degrees +// difference() { +// pie_slice(ang=70, h=50, d=100, center=true); +// #rounding_edge_mask(h=51, r=20.0, ang=70, $fn=32); +// } +// Example: Varying Rounding Radius +// difference() { +// pie_slice(ang=70, h=50, d=100, center=true); +// #rounding_angled_edge_mask(h=51, r1=10, r2=25, ang=70, $fn=32); +// } +// Example: Rounding a non-right angled edge, with a zero radius at the bottom. +// difference(){ +// linear_extrude(height=50)xflip(x=25)right_triangle([50,50]); +// rounding_edge_mask(l=51, ang=45, r1=0, r2=15, anchor=BOT); // } // Example: Masking by Attachment // diff() @@ -224,38 +247,82 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // rounding_edge_mask(l=p.z, r=25); // } // } -function rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask"); -module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) + +function rounding_edge_mask(l, r, ang=90, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask"); +module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, h, height, anchor=CENTER, spin=0, orient=UP, + _remove_tag=true) { - l = one_defined([l, h, height, length], "l,h,height,length"); - 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 = quantup(segs(max(r1,r2)),4); - default_tag("remove") { - attachable(anchor,spin,orient, size=[2*r1,2*r1,l], size2=[2*r2,2*r2]) { - if (r10 && ang<180, "ang must be a number between 0 and 180"); + steps = ceil(segs(r)*(180-ang)/360); + function make_path(r) = + let( + arc = r==0 ? repeat([0,0],steps+1) + : arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]), + maxx = last(arc).x, + maxy = arc[0].y, + cp = [-excess/tan(ang/2),-excess] + ) + [ + [maxx, -excess], + cp, + arc[0] + polar_to_xy(excess, 90+ang), + each arc + ]; + path1 = path3d(make_path(r1),-length/2); + path2 = path3d(make_path(r2),length/2); + left_normal = cylindrical_to_xyz(1,90+ang,0); + left_dir = cylindrical_to_xyz(1,ang,0); + zdir = unit([length, 0,-(r2-r1)/tan(ang/2)]); + cutfact = 1/sin(ang/2)-1; + + v=unit(zrot(ang,zdir)+left_normal); + ref = UP - (v*UP)*v; + backleft_spin=-vector_angle(rot(from=UP,to=v,p=BACK),ref); + + override = [ + [CENTER, [CENTER,UP]], + [TOP, [[0,0,length/2]]], + [BOT, [[0,0,-length/2]]], + [FWD, [[(r1+r2)/tan(ang/2)/4,0,0]]], + [FWD+BOT, [[r1/tan(ang/2)/2,0,-length/2]]], + [FWD+TOP, [[r2/tan(ang/2)/2,0,length/2]]], + [LEFT, [(r1+r2)/tan(ang/2)/4*left_dir, left_normal,ang-180]], + [LEFT+BOT, [down(length/2,r1/tan(ang/2)/2*left_dir), rot(v=left_dir,-45,p=left_normal),ang-180]], + [LEFT+TOP, [up(length/2,r2/tan(ang/2)/2*left_dir), rot(v=left_dir, 45, p=left_normal),ang-180]], + [LEFT+FWD, [CENTER, left_normal+FWD,ang/2-90]], + [LEFT+FWD+TOP, [[0,0,length/2], left_normal+FWD+UP,ang/2-90]], + [LEFT+FWD+BOT, [[0,0,-length/2], left_normal+FWD+DOWN,ang/2-90]], + [RIGHT, [[(r1+r2)/2/tan(ang/2),0,0],zdir]], + [RIGHT+TOP, [[r2/tan(ang/2),0,length/2],zdir+UP]], + [RIGHT+BOT, [[r1/tan(ang/2),0,-length/2],zdir+DOWN]], + [RIGHT+FWD, [[(r1+r2)/2/tan(ang/2),0,0],zdir+FWD]], + [RIGHT+TOP+FWD, [[r2/tan(ang/2),0,length/2],zdir+UP+FWD]], + [RIGHT+BOT+FWD, [[r1/tan(ang/2),0,-length/2],zdir+DOWN+FWD]], + [BACK, [ (r1+r2)/2/tan(ang/2)*left_dir,zrot(ang,zdir),ang+90]], + [BACK+BOT, [ down(length/2,r1/tan(ang/2)*left_dir),zrot(ang,zdir)+DOWN,ang+90]], + [BACK+UP, [ up(length/2,r2/tan(ang/2)*left_dir),zrot(ang,zdir)+UP,ang+90]], + [BACK+LEFT, [ (r1+r2)/2/tan(ang/2)*left_dir,zrot(ang,zdir)+left_normal, backleft_spin]], + [BACK+BOT+LEFT, [ down(length/2,r1/tan(ang/2)*left_dir),zrot(ang,zdir)+left_normal+DOWN,backleft_spin]], + [BACK+UP+LEFT, [ up(length/2,r2/tan(ang/2)*left_dir),zrot(ang,zdir)+left_normal+UP,backleft_spin]], + [BACK+RIGHT, [cylindrical_to_xyz(cutfact*(r1+r2)/2,ang/2,0), zrot(ang/2,zdir),ang/2+90]], + [BACK+RIGHT+TOP, [cylindrical_to_xyz(cutfact*r2,ang/2,length/2), zrot(ang/2,zdir)+UP,ang/2+90]], + [BACK+RIGHT+BOT, [cylindrical_to_xyz(cutfact*r1,ang/2,-length/2), zrot(ang/2,zdir)+DOWN,ang/2+90]], + ]; + vnf = vnf_vertex_array([path1,path2],caps=true,col_wrap=true); + default_tag("remove", _remove_tag) + attachable(anchor,spin,orient,size=[1,1,length],override=override){ + vnf_polyhedron(vnf); + children(); + } } + // Module: rounding_corner_mask() // Synopsis: Creates a shape to round 90° corners. // SynTags: Geom @@ -314,79 +381,12 @@ module rounding_corner_mask(r, d, style="octa", excess=0.1, anchor=CENTER, spin= } -// Module: rounding_angled_edge_mask() -// Synopsis: Creates a shape to round edges of any angle. -// SynTags: Geom -// Topics: Masks, Rounding -// See Also: rounding_angled_corner_mask(), rounding_edge_mask(), rounding_corner_mask(), default_tag(), diff() -// Usage: -// rounding_angled_edge_mask(h|l=|length=|height=, r|d=, [ang=]) [ATTACHMENTS]; -// rounding_angled_edge_mask(h|l=|length=|height=, r1=|d1=, r2=|d2=, [ang=]) [ATTACHMENTS]; -// Description: -// Creates a vertical mask that can be used to round the edge where two face meet, at any arbitrary -// angle. Difference it from the object to be rounded. The center of the mask should align exactly -// with the edge to be rounded. -// Arguments: -// h/l/height/length = Height of vertical mask. -// r = Radius of the rounding. -// --- -// r1 = Bottom radius of rounding. -// r2 = Top radius of rounding. -// d = Diameter of the rounding. -// d1 = Bottom diameter of rounding. -// d2 = Top diameter of rounding. -// ang = Angle that the planes meet at. Default: 90 -// 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` -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// Example: -// difference() { -// pie_slice(ang=70, h=50, d=100, center=true); -// #rounding_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32); -// } -// Example: Varying Rounding Radius -// difference() { -// pie_slice(ang=70, h=50, d=100, center=true); -// #rounding_angled_edge_mask(h=51, r1=10, r2=25, ang=70, $fn=32); -// } function rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length) = no_function("rounding_angled_edge_mask"); module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length) { - function _mask_shape(r) = [ - for (i = [0:1:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r], - for (i = [0:1:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r], - [min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r], - [min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r], - ]; - h = one_defined([l, h, height, length], "l,h,height,length"); - sweep = 180-ang; - 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); - n = ceil(segs(max(r1,r2))*sweep/360); - x = sin(90-(ang/2))/sin(ang/2) * (r1=0 && anchor.y>=0 ? undef - : - [[max(0,anchor.x)*maxx, max(0,anchor.y)*maxy, anchor.z*l/2]]; - attachable(anchor,spin,orient, size=[2*maxx,2*maxy,l],override=override) { - if (l > 0) { - linear_extrude(height=l, convexity=4, center=true) { - polygon(path); - } - } - children(); - } -} +function fillet(l, r, ang, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("fillet"); +module fillet(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor=CENTER, spin=0, orient=UP) +{ + rounding_edge_mask(l=l, r1=r1, r2=r2, ang=ang, excess=excess, d1=d1, d2=d2,d=d,r=r,length=length, h=h, height=height, + anchor=anchor, spin=spin, orient=orient, _remove_tag=false) + children(); +} // Function&Module: heightfield()