diff --git a/gears.scad b/gears.scad index 109bfd4..8674b45 100644 --- a/gears.scad +++ b/gears.scad @@ -2395,7 +2395,7 @@ function enveloping_worm( assert(is_finite(gear_spin)) let( hsteps = segs(d/2), - vsteps = hsteps*3, + vsteps = hsteps, helical = asin(starts * circ_pitch / PI / d), pr = pitch_radius(circ_pitch, mate_teeth, helical=helical), taper_table = taper @@ -2647,11 +2647,11 @@ function worm_gear( u = i / oslices, w_ang = worm_arc * (u - 0.5), g_ang_delta = w_ang/360 * tang * worm_starts * (left_handed?1:-1), - m = zrot(dir*(rteeth-0.0)*tang, cp=[worm_diam/2+pr,0,0]) * + m = zrot(dir*rteeth*tang+g_ang_delta, cp=[worm_diam/2+pr,0,0]) * left(crowning) * yrot(w_ang) * right(worm_diam/2+crowning) * - zrot(-1*dir*(rteeth+0.0)*tang+g_ang_delta, cp=[pr,0,0]) * + zrot(-dir*rteeth*tang+g_ang_delta, cp=[pr,0,0]) * xrot(180) ) apply(m, point3d(pt)) ] @@ -2674,7 +2674,7 @@ function worm_gear( twang1 = v_theta(truncrows[0][0]), twang2 = v_theta(last(truncrows[0])), twang = modang(twang1 - twang2) / (maxz-minz), - resampled_rows = [for (row = truncrows) resample_path(row, n=slices, closed=false)], + resampled_rows = [for (row = truncrows) resample_path(row, n=slices, keep_corners=30, closed=false)], tooth_rows = [ for (row = resampled_rows) [ zrot(twang*(zmax-row[0].z), p=[row[0].x, row[0].y, zmax]), @@ -2963,7 +2963,7 @@ function _gear_tooth_profile( // Reduce number of vertices. tooth = path_merge_collinear( - resample_path(full_tooth, n=ceil(2*steps), closed=false) + resample_path(full_tooth, n=ceil(2*steps), keep_corners=30, closed=false) ), out = center? fwd(prad, p=tooth) : tooth @@ -3944,7 +3944,7 @@ module _show_gear_tooth_profile( stroke([polar_to_xy(min(rr,br)-mod/10,90+180/teeth),polar_to_xy(or+mod/10,90+180/teeth)], width=0.05, closed=true); } zrot_copies([0]) { // Tooth profile overlay - stroke(tooth, width=0.1, dots=(show_verts?"dot":false), endcap_color1="green"); + stroke(tooth, width=0.1, dots=(show_verts?"dot":false), endcap_color1="green", endcap_color2="red"); } } } diff --git a/geometry.scad b/geometry.scad index a9f126d..4e16674 100644 --- a/geometry.scad +++ b/geometry.scad @@ -168,14 +168,14 @@ function is_collinear(a, b, c, eps=EPSILON) = // Topics: Geometry, Points, Lines, Distance // See Also: is_collinear(), is_point_on_line(), point_line_distance(), line_from_points() // Usage: -// dist = point_line_distance(line, pt, [bounded]); +// dist = point_line_distance(pt, line, [bounded]); // Description: // Finds the shortest distance from the point `pt` to the specified line, segment or ray. // The bounded parameter specifies the whether the endpoints give a ray or segment. // By default assumes an unbounded line. // Arguments: -// line = A list of two points defining a line. // pt = A point to find the distance of from the line. +// line = A list of two points defining a line. // bounded = a boolean or list of two booleans specifiying whether each end is bounded. Default: false // Example: // dist1 = point_line_distance([3,8], [[-10,0], [10,0]]); // Returns: 8 diff --git a/paths.scad b/paths.scad index 6772ab0..28b439f 100644 --- a/paths.scad +++ b/paths.scad @@ -473,7 +473,8 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) = // Description: // Compute a uniform resampling of the input path. If you specify `n` then the output path will have n // points spaced uniformly (by linear interpolation along the input path segments). The only points of the -// input path that are guaranteed to appear in the output path are the starting and ending points. +// input path that are guaranteed to appear in the output path are the starting and ending points, and any +// points that have an angular deflection of at least the number of degrees given in `keep_corners`. // If you specify `spacing` then the length you give will be rounded to the nearest spacing that gives // a uniform sampling of the path and the resulting uniformly sampled path is returned. // Note that because this function operates on a discrete input path the quality of the output depends on @@ -483,6 +484,7 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) = // n = Number of points in output // --- // spacing = Approximate spacing desired +// keep_corners = If given a scalar, path vertices with deflection angle greater than this are preserved in the output. // closed = set to true if path is closed. Default: true // Example(2D): Subsampling lots of points from a smooth curve // path = xscale(2,circle($fn=250, r=10)); @@ -494,35 +496,67 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) = // sampled = resample_path(path, spacing=17); // stroke(path); // color("red")move_copies(sampled) circle($fn=16); -// Example(2D): Notice that the corners are excluded +// Example(2D): Notice that the corners are excluded. // path = square(20); // sampled = resample_path(path, spacing=6); // stroke(path,closed=true); // color("red")move_copies(sampled) circle($fn=16); +// Example(2D): Forcing preservation of corners. +// path = square(20); +// sampled = resample_path(path, spacing=6, keep_corners=90); +// stroke(path,closed=true); +// color("red")move_copies(sampled) circle($fn=16); // Example(2D): Closed set to false // path = square(20); // sampled = resample_path(path, spacing=6,closed=false); // stroke(path); // color("red")move_copies(sampled) circle($fn=16); - -function resample_path(path, n, spacing, closed=true) = - let(path = force_path(path)) - assert(is_path(path)) - assert(num_defined([n,spacing])==1,"Must define exactly one of n and spacing") - assert(is_bool(closed)) - let( - length = path_length(path,closed), - // In the open path case decrease n by 1 so that we don't try to get - // path_cut to return the endpoint (which might fail due to rounding) - // Add last point later - n = is_def(n) ? n-(closed?0:1) : round(length/spacing), - distlist = lerpn(0,length,n,false), - cuts = path_cut_points(path, distlist, closed=closed) - ) - [ each column(cuts,0), - if (!closed) last(path) // Then add last point here - ]; +function resample_path(path, n, spacing, keep_corners, closed=true) = + let(path = force_path(path)) + assert(is_path(path)) + assert(num_defined([n,spacing])==1,"Must define exactly one of n and spacing") + assert(n==undef || (is_integer(n) && n>0)) + assert(spacing==undef || (is_finite(spacing) && spacing>0)) + assert(is_bool(closed)) + let( + corners = is_undef(keep_corners) + ? [0, len(path)-(closed?0:1)] + : [ + 0, + for (i = [1:1:len(path)-(closed?1:2)]) + let( ang = abs(modang(vector_angle(select(path,i-1,i+1))-180)) ) + if (ang >= keep_corners) i, + len(path)-(closed?0:1), + ], + pcnt = len(path), + plen = path_length(path, closed=closed), + subpaths = [ for (p = pair(corners)) [for(i = [p.x:1:p.y]) path[i%pcnt]] ], + n = is_undef(n)? n : closed? n+1 : n + ) + assert(n==undef || n >= len(corners), "There are nore than `n=` corners whose angle is greater than `keep_corners=`.") + let( + lens = [for (subpath = subpaths) path_length(subpath)], + part_ns = is_undef(n) + ? [for (i=idx(subpaths)) ceil(lens[i]/spacing)-1] + : let( + ccnt = len(corners), + parts = [for (l=lens) (n-ccnt) * l/plen], + ) + _sum_preserving_round(parts), + out = [ + for (i = idx(subpaths)) + let( + subpath = subpaths[i], + splen = lens[i], + n = part_ns[i] + 1, + distlist = lerpn(0, splen, n, false), + cuts = path_cut_points(subpath, distlist, closed=false) + ) + each column(cuts,0), + if (!closed) last(path) + ] + ) out; // Section: Path Geometry