diff --git a/arrays.scad b/arrays.scad index 604186b..40b232f 100644 --- a/arrays.scad +++ b/arrays.scad @@ -425,11 +425,15 @@ function repeat(val, n, i=0) = // n = The length of the list of numbers to create. // s = The starting value of the list of numbers. // step = The amount to increment successive numbers in the list. +// reverse = Reverse the list. Default: false. // Example: // nl1 = count(5); // Returns: [0,1,2,3,4] // nl2 = count(5,3); // Returns: [3,4,5,6,7] // nl3 = count(4,3,2); // Returns: [3,5,7,9] -function count(n,s=0,step=1) = [for (i=[0:1:n-1]) s+i*step]; +// nl4 = count(5,reverse=true); // Returns: [4,3,2,1,0] +// nl5 = count(5,3,reverse=true); // Returns: [7,6,5,4,3] +function count(n,s=0,step=1,reverse=false) = reverse? [for (i=[n-1:-1:0]) s+i*step] + : [for (i=[0:1:n-1]) s+i*step]; diff --git a/beziers.scad b/beziers.scad index 7cda75b..90b9790 100644 --- a/beziers.scad +++ b/beziers.scad @@ -1328,7 +1328,6 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false, return_ed let( row_degen = [for(row=patch) all_equal(row)], col_degen = [for(col=transpose(patch)) all_equal(col)], - top_degen = row_degen[0], bot_degen = last(row_degen), left_degen = col_degen[0], @@ -1362,7 +1361,7 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false, return_ed for(j=[0:splinesteps-2]) bezier_points(subindex(bpatch,j+1), lerpn(0,1,rowcount[j])), [last(bpatch[0])] ], - vnf = vnf_tri_array(pts, reverse=reverse) + vnf = vnf_tri_array(pts, reverse=!reverse) ) [ vnf, [ @@ -1390,7 +1389,7 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false, return_ed [bpatch[0][0]], for(j=[1:splinesteps]) bezier_points(subindex(bpatch,j), lerpn(0,1,rowmax[j]+1)) ], - vnf = vnf_tri_array(pts, reverse=reverse) + vnf = vnf_tri_array(pts, reverse=!reverse) ) [ vnf, [ diff --git a/common.scad b/common.scad index 6d2d44f..4ec8762 100644 --- a/common.scad +++ b/common.scad @@ -6,6 +6,7 @@ ////////////////////////////////////////////////////////////////////// + // Section: Type handling helpers. @@ -323,7 +324,8 @@ function first_defined(v,recursive=false,_i=0) = // Examples: // length = one_defined([length,L,l], ["length","L","l"]); // length = one_defined([length,L,l], "length,L,l", dflt=1); -function one_defined(vals, names, dflt=_UNDEF) = + +function one_defined(vals, names, dflt=_UNDEF) = let( checkargs = is_list(names)? assert(len(vals) == len(names)) : is_string(names)? let( @@ -534,10 +536,6 @@ function get_radius(r1, r2, r, d1, d2, d, dflt) = // echo(f(undef, arg1="given1", undef)); // // ["given1", undef, undef] -// a value that the user should never enter randomly; -// result of `dd if=/dev/random bs=32 count=1 |base64` : -_UNDEF="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI"; - /* Note: however tempting it might be, it is *not* possible to accept * named argument as a list [named1, named2, ...] (without default * values), because the values [named1, named2...] themselves might be diff --git a/constants.scad b/constants.scad index cc6daf1..8815aa9 100644 --- a/constants.scad +++ b/constants.scad @@ -5,6 +5,9 @@ // include ////////////////////////////////////////////////////////////////////// +// a value that the user should never enter randomly; +// result of `dd if=/dev/random bs=32 count=1 |base64` : +_UNDEF="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI"; // Section: General Constants diff --git a/paths.scad b/paths.scad index 11d05dd..9fa8d73 100644 --- a/paths.scad +++ b/paths.scad @@ -380,7 +380,7 @@ function path_normals(path, tangents, closed=false) = dim == 2 ? [tangents[i].y,-tangents[i].x] : let(v=cross(cross(pts[1]-pts[0], pts[2]-pts[0]),tangents[i])) assert(norm(v)>EPSILON, "3D path contains collinear points") - v + unit(v) ]; diff --git a/rounding.scad b/rounding.scad index d2493c6..0b980b3 100644 --- a/rounding.scad +++ b/rounding.scad @@ -1779,8 +1779,8 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) = // M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right", "move", "right", "move", 4, "right", "move", 3, "right", "move", 2])); // rounded_prism(M, apply(right(1)*scale(.75)*up(3),M), joint_top=0.5, joint_bot=0.2, joint_sides=[.2,1,1,0.5,1.5,.5,2], splinesteps=32); // Example: this example shows most of the different types of patches that rounded_prism creates. Note that some of the patches are close to interfering with each other across the top of the polyhedron, which would create an invalid result. - N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2])); - rounded_prism(N, height=3, joint_bot=0.5, joint_top=1.25, joint_sides=[[1,1.75],0,.5,.5,2], debug=true); +// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2])); +// rounded_prism(N, height=3, joint_bot=0.5, joint_top=1.25, joint_sides=[[1,1.75],0,.5,.5,2], debug=true); // Example: This object has different scales on its different axies. Here is the largest symmetric rounding that fits. Note that the rounding is slightly smaller than the object dimensions because of roundoff error. // rounded_prism(square([100.1,30.1]), height=8.1, joint_top=4, joint_bot=4, joint_sides=15, k_sides=0.3, splinesteps=32); // Example: Using asymetric rounding enables a much more rounded form: @@ -1886,8 +1886,8 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b let( // Entries in the next two lists have the form [edges, vnf] where // edges is a list [leftedge, rightedge, topedge, botedge] - top_samples = [for(patch=top_patch) bezier_patch_degenerate(patch,splinesteps,reverse=true,return_edges=true) ], - bot_samples = [for(patch=bot_patch) bezier_patch_degenerate(patch,splinesteps,reverse=false,return_edges=true) ], + top_samples = [for(patch=top_patch) bezier_patch_degenerate(patch,splinesteps,reverse=false,return_edges=true) ], + bot_samples = [for(patch=bot_patch) bezier_patch_degenerate(patch,splinesteps,reverse=true,return_edges=true) ], leftidx=0, rightidx=1, topidx=2, diff --git a/skin.scad b/skin.scad index cf75796..8b197f9 100644 --- a/skin.scad +++ b/skin.scad @@ -5,7 +5,6 @@ // - https://github.com/openscad/list-comprehension-demos/blob/master/skin.scad // Includes: // include -// include ////////////////////////////////////////////////////////////////////// @@ -13,9 +12,9 @@ // Function&Module: skin() // Usage: As module: -// skin(profiles, slices, , , , , , , , ,,,,) ; +// skin(profiles, slices, , , , , , , , , ,,,,) ; // Usage: As function: -// vnf = skin(profiles, slices, , , , , , ); +// vnf = skin(profiles, slices, , , , , , , ); // Description: // Given a list of two or more path `profiles` in 3d space, produces faces to skin a surface between // the profiles. Optionally the first and last profiles can have endcaps, or the first and last profiles @@ -152,6 +151,7 @@ // orient = Vector to rotate top towards after spin (module only) // extent = use extent method for computing anchors. (module only) Default: false // cp = set centerpoint for anchor computation. (module only) Default: object centroid +// style = vnf_vertex_array style. Default: "min_edge" // Example: // skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10); // Example: Rotating the pentagon place the zero index at different locations, giving a twist @@ -376,10 +376,10 @@ // stroke(zrot(30, p=yscale(0.5, p=circle(d=120))),width=10,closed=true); // } // } -module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, convexity=10, +module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge", convexity=10, anchor="origin",cp,spin=0, orient=UP, extent=false) -{ - vnf = skin(profiles, slices, refine, method, sampling, caps, closed, z); +{ + vnf = skin(profiles, slices, refine, method, sampling, caps, closed, z, style=style); attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : vnf_centroid(vnf)) { vnf_polyhedron(vnf,convexity=convexity); @@ -388,7 +388,7 @@ module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed= } -function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z) = +function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge") = assert(is_def(slices),"The slices argument must be specified.") assert(is_list(profiles) && len(profiles)>1, "Must provide at least two profiles") let( bad = [for(i=idx(profiles)) if (!(is_path(profiles[i]) && len(profiles[i])>2)) i]) @@ -464,7 +464,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close : reindex_polygon(resampled[i-1],resampled[i])], sliced = slice_profiles(fixedprof, slices, closed) ) - !closed ? sliced : concat(sliced,[sliced[0]]) + [!closed ? sliced : concat(sliced,[sliced[0]])] : // There are duplicators, so use approach where each pair is treated separately [for(i=[0:profcount-1]) let( @@ -481,63 +481,59 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close ". Method ",method[i]," requires equal values")) refine[i] * len(pair[0]) ) - each subdivide_and_slice(pair,slices[i], nsamples, method=sampling)] + subdivide_and_slice(pair,slices[i], nsamples, method=sampling)] ) - _skin_core(full_list, caps=fullcaps); - - + vnf_merge(cleanup=false, + [for(i=idx(full_list)) + vnf_vertex_array(full_list[i], cap1=i==0 && fullcaps[0], cap2=i==len(full_list)-1 && fullcaps[1], + col_wrap=true, style=style)]); function _skin_core(profiles, caps) = let( - vertices = [for (prof=profiles) each prof], - plens = [for (prof=profiles) len(prof)], - sidefaces = [ + vertices = flatten(profiles), + plen = len(profiles[0]), + faces = [ for(pidx=idx(profiles,e=-2)) let( - prof1 = profiles[pidx%len(profiles)], - prof2 = profiles[(pidx+1)%len(profiles)], - voff = default(sum([for (i=[0:1:pidx-1]) plens[i]]),0), + prof1 = profiles[pidx], + prof2 = profiles[pidx+1], + voff = pidx*plen, faces = [ for( first = true, finishing = false, finished = false, - plen1 = len(prof1), - plen2 = len(prof2), - i=0, j=0, side=0; + i=0, j=0, side=false; !finished; side = let( - p1a = prof1[(i+0)%plen1], - p1b = prof1[(i+1)%plen1], - p2a = prof2[(j+0)%plen2], - p2b = prof2[(j+1)%plen2], + p1a = prof1[i%plen], + p1b = prof1[(i+1)%plen], + p2a = prof2[j%plen], + p2b = prof2[(j+1)%plen], dist1 = norm(p1a-p2b), dist2 = norm(p1b-p2a) - ) (i==j) ? (dist1>dist2? 1 : 0) : (idist2 : i=plen1 && j>=plen2 + finishing = i>=plen && j>=plen ) if (!first) face ] - ) each faces - ], - firstcap = !caps[0] ? [] : let( - prof1 = profiles[0] - ) [[for (i=idx(prof1)) plens[0]-1-i]], - secondcap = !caps[1] ? [] : let( - prof2 = last(profiles), - eoff = sum(list_head(plens)) - ) [[for (i=idx(prof2)) eoff+i]] - ) [vertices, concat(sidefaces,firstcap,secondcap)]; + ) each faces, + if (caps[0]) count(plen,reverse=true), + if (caps[1]) count(plen,plen*(len(profiles)-1)) + ] + ) [vertices, faces]; + + @@ -903,9 +899,9 @@ function associate_vertices(polygons, split, curpoly=0) = // Function&Module: sweep() // Usage: As Module -// sweep(shape, transforms, , , , , , , ) ; +// sweep(shape, transforms, , ,