From 036da1a3ef888a92b8a9412612fd3c10d58bfd0d Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Thu, 2 Mar 2023 19:40:12 -0500 Subject: [PATCH] Rename close_path and cleanup_path to list_wrap and list_unwrap --- comparisons.scad | 41 +++++++++++++++ drawing.scad | 4 +- geometry.scad | 4 +- paths.scad | 26 ++-------- regions.scad | 4 +- rounding.scad | 6 +-- skin.scad | 99 ++++++++++++++++++++++++++++++------- tests/test_comparisons.scad | 20 ++++++++ tests/test_paths.scad | 15 ------ threading.scad | 13 +++-- vnf.scad | 2 +- 11 files changed, 164 insertions(+), 70 deletions(-) diff --git a/comparisons.scad b/comparisons.scad index 2712c25..bd2906a 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -464,6 +464,47 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = +// Function: list_wrap() +// Usage: +// list_wrap(path, [eps]); +// Description: +// Force a list to wrap around so that its last point is equal to its first point: if the first and last entries are equal, simply returns the list unchanged. +// Otherwise returns the list with the first point duplicated at the end of the list. Comparisons are done to the tolerance `eps`. Lists of length 0 or +// 1 are returned unchanged. +// Arguments: +// list = list to unwrap +// eps = epsilon for comparison. Default: EPSILON (1e-9) +// See Also: list_unwrap(), deduplicate() + +function list_wrap(list, eps=EPSILON) = + assert(is_list(list)) + len(list)<2 || are_ends_equal(list,eps=eps)? list : [each list, list[0]]; + + +function cleanup_path(list,eps=EPSILON) = + echo("***** Function cleanup_path() has been replaced by list_unwrap() and will be removed in a future version *****") + list_unwrap(list,eps); + +function close_path(list,eps=EPSILON) = + echo("***** Function close_path() has been replaced by list_wrap() and will be removed in a future version *****") + list_wrap(list,eps); + +// Function: list_unwrap() +// Usage: +// list_unwrap(list, [eps]); +// Description: +// If a list's last point matches its first point then delete the last point. Inverse operation to {{list_wrap()}}. Note that if the first/last points +// are repeated then the output may still have the first point equal to the last point. Comparisons are done to the tolerance `eps`. If the list has +// length 0 or 1 it is returned unchanged. +// Arguments: +// list = list to unwrap +// eps = epsilon for comparison. Default: EPSILON (1e-9) +function list_unwrap(list, eps=EPSILON) = + assert(is_list(list)) + len(list)>=2 && are_ends_equal(list,eps=eps)? [for (i=[0:1:len(list)-2]) list[i]] : list; + + + // Function: unique() // Usage: // ulist = unique(list); diff --git a/drawing.scad b/drawing.scad index 3c92f1b..62738e5 100644 --- a/drawing.scad +++ b/drawing.scad @@ -285,7 +285,7 @@ module stroke( for (path = paths) { pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]); assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region."); - path = deduplicate( closed? close_path(path) : path ); + path = deduplicate( closed? list_wrap(path) : path ); check4 = assert(is_num(width) || len(width)==len(path), "width must be a number or a vector the same length as the path (or all components of a region)"); @@ -576,7 +576,7 @@ function dashed_stroke(path, dashpat=[3,3], closed=false, fit=true, mindash=0.5) each dashed_stroke(p, dashpat, closed=true, fit=fit) ] : let( - path = closed? close_path(path) : path, + path = closed? list_wrap(path) : path, dashpat = len(dashpat)%2==0? dashpat : concat(dashpat,[0]), plen = path_length(path), dlen = sum(dashpat), diff --git a/geometry.scad b/geometry.scad index 0ff8c7f..53d5dd3 100644 --- a/geometry.scad +++ b/geometry.scad @@ -2217,8 +2217,8 @@ function align_polygon(reference, poly, angles, cp, trans, return_ind=false) = // rot(90, p=pentagon(r=4))); // returns false function are_polygons_equal(poly1, poly2, eps=EPSILON) = let( - poly1 = cleanup_path(poly1), - poly2 = cleanup_path(poly2), + poly1 = list_unwrap(poly1), + poly2 = list_unwrap(poly2), l1 = len(poly1), l2 = len(poly2) ) l1 != l2 ? false : diff --git a/paths.scad b/paths.scad index cf5d9de..8dc1b94 100644 --- a/paths.scad +++ b/paths.scad @@ -87,24 +87,6 @@ function force_path(path, name="path") = : path; -// Function: close_path() -// Usage: -// close_path(path); -// Description: -// If a path's last point does not coincide with its first point, closes the path so it does. -function close_path(path, eps=EPSILON) = - are_ends_equal(path,eps=eps)? path : concat(path,[path[0]]); - - -// Function: cleanup_path() -// Usage: -// cleanup_path(path); -// Description: -// If a path's last point coincides with its first point, deletes the last point in the path. -function cleanup_path(path, eps=EPSILON) = - are_ends_equal(path,eps=eps)? [for (i=[0:1:len(path)-2]) path[i]] : path; - - /// Internal Function: _path_select() /// Usage: /// _path_select(path,s1,u1,s2,u2,[closed]): @@ -260,7 +242,7 @@ function path_length_fractions(path, closed) = /// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10); function _path_self_intersections(path, closed=true, eps=EPSILON) = let( - path = closed ? close_path(path,eps=eps) : path, + path = closed ? list_wrap(path,eps=eps) : path, plen = len(path) ) [ for (i = [0:1:plen-3]) let( @@ -919,7 +901,7 @@ function split_path_at_self_crossings(path, closed=true, eps=EPSILON) = assert(is_path(path,2), "Must give a 2D path") assert(is_bool(closed)) let( - path = cleanup_path(path, eps=eps), + path = list_unwrap(path, eps=eps), isects = deduplicate( eps=eps, concat( @@ -1030,7 +1012,7 @@ function polygon_parts(poly, nonzero=false, eps=EPSILON) = assert(is_path(poly,2), "Must give 2D polygon") assert(is_bool(nonzero)) let( - poly = cleanup_path(poly, eps=eps), + poly = list_unwrap(poly, eps=eps), tagged = _tag_self_crossing_subpaths(poly, nonzero=nonzero, closed=true, eps=eps), kept = [for (sub = tagged) if(sub[0] == "O") sub[1]], outregion = _assemble_path_fragments(kept, eps=eps) @@ -1158,7 +1140,7 @@ function _assemble_path_fragments(fragments, eps=EPSILON, _finished=[]) = l_area = abs(polygon_area(result_l[0])), r_area = abs(polygon_area(result_r[0])), result = l_area < r_area? result_l : result_r, - newpath = cleanup_path(result[0]), + newpath = list_unwrap(result[0]), remainder = result[1], finished = min(l_area,r_area)=0 && min(signs)<=0) // some edge intersects line [a1,a2] diff --git a/rounding.scad b/rounding.scad index 8ef8d36..eb4dac2 100644 --- a/rounding.scad +++ b/rounding.scad @@ -678,7 +678,7 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals bez = path_to_bezpath(path, tangents=tangents, size=size, relsize=relsize, uniform=uniform, closed=default(closed,false)), smoothed = bezpath_curve(bez,splinesteps=splinesteps) ) - closed ? cleanup_path(smoothed) : smoothed; + closed ? list_unwrap(smoothed) : smoothed; function _scalar_to_vector(value,length,varname) = @@ -815,7 +815,7 @@ function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)= let( paths = !closed || len(paths)>1 ? paths - : [close_path(paths[0])], + : [list_wrap(paths[0])], N = len(paths) + (closed?0:-1), k = _scalar_to_vector(k,N), repjoint = is_num(joint) || (is_vector(joint,2) && len(paths)!=3), @@ -828,7 +828,7 @@ function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)= ) assert(bad_j==[], str("Invalid joint values at indices ",bad_j)) let(result=_path_join(paths,joint,k, relocate=relocate, closed=closed)) - closed ? cleanup_path(result) : result; + closed ? list_unwrap(result) : result; function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) = let( diff --git a/skin.scad b/skin.scad index 2e495e0..afe7095 100644 --- a/skin.scad +++ b/skin.scad @@ -771,7 +771,7 @@ function linear_sweep( trgns = [ for (rgn = regions) [ for (path = rgn) let( - p = cleanup_path(path), + p = list_unwrap(path), path = is_undef(maxseg)? p : [ for (seg = pair(p,true)) each let( steps = ceil(norm(seg.y - seg.x) / maxseg) ) @@ -783,7 +783,7 @@ function linear_sweep( vnf = vnf_join([ for (rgn = regions) for (pathnum = idx(rgn)) let( - p = cleanup_path(rgn[pathnum]), + p = list_unwrap(rgn[pathnum]), path = is_undef(maxseg)? p : [ for (seg=pair(p,true)) each let(steps=ceil(norm(seg.y-seg.x)/maxseg)) @@ -1078,12 +1078,75 @@ module rotate_sweep( // Example: // poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]]; // spiral_sweep(poly, h=200, r=50, turns=3, $fn=36); -function _taperfunc(x) = +function _taperfunc_orig_1d(x,L) = x>1 ? 1 : x<0 ? 0: - let(higofs = pow(0.05,2)) // Smallest hig scale is the square root of this value + let( + higofs = pow(0.05,2) // Smallest hig scale is the square root of this value + ) sqrt((1-higofs)*x+higofs); + +function _taperfunc_orig(x,L) = + let(s=_taperfunc_orig_1d(x)) + x>1 ? [1,1] + : x<0 ? [0,0] + + : [lerp(s,1,.25),s]; function _taperfunc_ellipse(x) = sqrt(1-(1-x)^2); +function _taperfunc_linear(x) = + x>1 ? 1 : x<0 ? 0 : x; +function _taperfunc_ogive_width(x,L) = + let( minscale = .2, + r=(L^2+(1-minscale^2))/2/(1-minscale), + scale = sqrt(r^2-(L*(1-x))^2) -(r-1) + ) + x>1 ? [1,1] + : x<0 ? [0,0] + : [scale,1]; +function _taperfunc_ogive_width_circle(x,L,h) = + let( minscale = .2, + r=(L^2+(1-minscale^2))/2/(1-minscale), + scale = sqrt(r^2-(L*(1-x))^2) -(r-1), + vscale = x*L>h ? h : sqrt(h^2-(x*L-h)^2) + ) + x>1 ? [1,1] + : x<0.02 ? [0,0] + : [scale,vscale/h]; +function _taperfunc_ogive_height(x,L) = + let( minscale = .1,L=3*L, + r=(L^2+(1-minscale^2))/2/(1-minscale), + scale = sqrt(r^2-(L*(1-x))^2) -(r-1) + ) + x>1 ? [1,1] + : x<0 ? [0,0] //minscale,0] + : [1,scale]; +function _taperfunc_ogive(x,L) = + let( minscale = .3, + r=(L^2+(1-minscale^2))/2/(1-minscale), + scale = sqrt(r^2-(L*(1-x))^2) -(r-1) + ) + x>1 ? [1,1] + : x<0 ? [0,0] + : [scale,scale]; +function _taperfunc_ogive_orig(x,L) = + let( minscale = .3, + r=(L^2+(1-minscale^2))/2/(1-minscale), + scale = sqrt(r^2-(L*(1-x))^2) -(r-1) + ) + x>1 ? [1,1] + : x<0 ? [0,0] + : [lerp(_taperfunc_orig_1d(x),1,.25),scale]; + +function _taperfunc_cut(x,L) = x>1 ? [1,1] : [0,0]; + + +function _taperfunc(x,L,h) = _taperfunc_ogive_width_circle(x,L,h); +//function _taperfunc(x,L,h) = _taperfunc_orig(x,L); +//function _taperfunc(x,L,h) = _taperfunc_ogive_width(x,L); +function _taperfunc(x,L,h) = _taperfunc_orig(x,L); + + + function _ss_polygon_r(N,theta) = let( alpha = 360/N ) cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2)); @@ -1144,16 +1207,16 @@ function spiral_sweep(poly, h, r, turns=1, taper, center, r1, r2, d, d1, d2, tap skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)), points = [ for (a = interp_ang) let ( - hsc = atapercut2 ? _taperfunc((maxang-a)/taperang2) - : 1, + hsc = atapercut2 ? _taperfunc((maxang-a)/taperang2,abs(taper2),xmax-xmin) + : [1,1], u = a/(360*turns), r = lerp(r1,r2,u), mat = affine3d_zrot(dir*a) * affine3d_translate([_ss_polygon_r(sides,dir*a)*r, 0, h * (u-0.5)]) * affine3d_xrot(90) * skewmat - * scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]), + * scale([hsc.y,hsc.x,1], cp=[internal ? xmax : xmin, yctr, 0]), pts = apply(mat, poly) ) pts ], @@ -3425,8 +3488,8 @@ function _textured_linear_sweep( : [ceil(6*plen/h), 6], obases = resample_path(path, n=counts.x * samples, closed=true), onorms = path_normals(obases, closed=true), - bases = close_path(obases), - norms = close_path(onorms), + bases = list_wrap(obases), + norms = list_wrap(onorms), vnf = is_vnf(texture) ? let( // VNF tile texture row_vnf = vnf_join([ @@ -3511,8 +3574,8 @@ function _textured_linear_sweep( : [ceil(6*plen/h), 6], obases = resample_path(path, n=counts.x * samples, closed=true), onorms = path_normals(obases, closed=true), - bases = close_path(obases), - norms = close_path(onorms), + bases = list_wrap(obases), + norms = list_wrap(onorms), nupath = [ for (j = [0:1:counts.x-1], vert = tpath) let( part = (j + vert.x) * samples, @@ -3699,8 +3762,8 @@ function _textured_revolution( is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6, obases = resample_path(path, n=counts_y * samples + (closed?0:1), closed=closed), onorms = path_normals(obases, closed=closed), - rbases = closed? close_path(obases) : obases, - rnorms = closed? close_path(onorms) : onorms, + rbases = closed? list_wrap(obases) : obases, + rnorms = closed? list_wrap(onorms) : onorms, bases = xrot(90, p=path3d(rbases)), norms = xrot(90, p=path3d(rnorms)), vnf = is_vnf(texture) @@ -3766,8 +3829,8 @@ function _textured_revolution( is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6, obases = resample_path(path, n=counts_y * samples + (closed?0:1), closed=closed), onorms = path_normals(obases, closed=closed), - bases = closed? close_path(obases) : obases, - norms = closed? close_path(onorms) : onorms, + bases = closed? list_wrap(obases) : obases, + norms = closed? list_wrap(onorms) : onorms, ppath = is_vnf(texture) ? [ // VNF tile texture for (j = [0:1:counts_y-1]) @@ -3816,8 +3879,8 @@ function _textured_revolution( is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6, obases = resample_path(rgn[0], n=counts_y * samples + (closed?0:1), closed=closed), onorms = path_normals(obases, closed=closed), - rbases = closed? close_path(obases) : obases, - rnorms = closed? close_path(onorms) : onorms, + rbases = closed? list_wrap(obases) : obases, + rnorms = closed? list_wrap(onorms) : onorms, bases = xrot(90, p=path3d(rbases)), norms = xrot(90, p=path3d(rnorms)), caps_vnf = vnf_join([ diff --git a/tests/test_comparisons.scad b/tests/test_comparisons.scad index 5bb85bc..d672c5f 100644 --- a/tests/test_comparisons.scad +++ b/tests/test_comparisons.scad @@ -81,6 +81,26 @@ test_unique_count(); + +module test_list_wrap() { + assert(list_wrap([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); + assert(list_wrap([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); + assert(list_wrap([])==[]); + assert(list_wrap([3])==[3]); +} +test_list_wrap(); + + +module test_list_unwrap() { + assert(list_unwrap([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); + assert(list_unwrap([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); + assert(list_unwrap([])==[]); + assert(list_unwrap([3])==[3]); +} +test_list_unwrap(); + + + module test_is_increasing() { assert(is_increasing([1,2,3,4]) == true); assert(is_increasing([1,2,2,2]) == true); diff --git a/tests/test_paths.scad b/tests/test_paths.scad index 885afbd..11e2c1a 100644 --- a/tests/test_paths.scad +++ b/tests/test_paths.scad @@ -32,21 +32,6 @@ module force_path() { test_is_1region(); - -module test_close_path() { - assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); - assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); -} -test_close_path(); - - -module test_cleanup_path() { - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); -} -test_cleanup_path(); - - module test_path_merge_collinear() { path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]]; assert(path_merge_collinear(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); diff --git a/threading.scad b/threading.scad index 7251aa3..465af98 100644 --- a/threading.scad +++ b/threading.scad @@ -1217,7 +1217,7 @@ module generic_threaded_rod( * frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis * scale(pitch); // scale profile by pitch start_steps = sides / starts; - higlen = 4/32*360;//360*max(pitch/2, pmax-depth)/(2*PI*_r2); + higlen = 2/32*360;//360*max(pitch/2, pmax-depth)/(2*PI*_r2); echo(higlen=higlen); thread_verts = [ // Outer loop constructs a vertical column of the screw at each angle @@ -1231,10 +1231,13 @@ module generic_threaded_rod( for (thread = [-threads/2:1:threads/2-1]) let( tang = thread/starts * 360 + ang, - hsc = tang < -twist/2+higang1 ? _taperfunc(1-(-twist/2+higang1-tang)/higlen ) - : tang > twist/2-higang2 ? _taperfunc(1-(tang-twist/2+higang2)/higlen ) - : 1, - higscale=scale([lerp(hsc,1,0.25),hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0]) + hsc = tang < -twist/2+higang1 ? _taperfunc(1-(-twist/2+higang1-tang)/higlen,PI*2*_r1*higlen/360 ) + : tang > twist/2-higang2 ? _taperfunc(1-(tang-twist/2+higang2)/higlen,PI*2*_r2*higlen/360 ) + : [1,1], + higscale=scale([hsc.x, hsc.y,1], cp=[0,internal ? pmax/pitch:-pdepth, 0]) +// higscale=scale([lerp(hsc,1,0.25),hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0]) +// higscale=scale([lerp(hsc,1,0.25),1,1], cp=[0,internal ? pmax/pitch:-pdepth, 0]) +// higscale=scale([1,hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0]) ) // The right movement finds the position of the thread along // what will be the z axis after the profile is mapped to 3d diff --git a/vnf.scad b/vnf.scad index 6f1dfe5..a17d807 100644 --- a/vnf.scad +++ b/vnf.scad @@ -826,7 +826,7 @@ function _split_polygon_at_x(poly, x) = if (len(out1)>=3) each split_path_at_self_crossings(out1), if (len(out2)>=3) each split_path_at_self_crossings(out2), ], - out = [for (p=out3) if (len(p) > 2) cleanup_path(p)] + out = [for (p=out3) if (len(p) > 2) list_unwrap(p)] ) out;