From dea5949df8e183a7310a7c7164bbca2e87372ba4 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 12 Apr 2021 20:15:01 -0400 Subject: [PATCH 1/5] Fix face orientation bugs in vnf_vertex_array, rounded_prism, bezier_patch_degenerate. Added reverse option to count(). --- arrays.scad | 6 ++- beziers.scad | 5 +-- rounding.scad | 8 ++-- vnf.scad | 118 ++++++++++++++++++++++++-------------------------- 4 files changed, 67 insertions(+), 70 deletions(-) diff --git a/arrays.scad b/arrays.scad index ac97444..cb37111 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/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/vnf.scad b/vnf.scad index 55095c8..abdcd8f 100644 --- a/vnf.scad +++ b/vnf.scad @@ -287,83 +287,77 @@ function vnf_vertex_array( reverse=false, style="default", vnf=EMPTY_VNF -) = - assert((!caps)||(caps&&col_wrap)) +) = + assert(!(any([caps,cap1,cap2]) && !col_wrap), "col_wrap must be true if caps are requested") + assert(!(any([caps,cap1,cap2]) && row_wrap), "Cannot combine caps with row_wrap") assert(in_list(style,["default","alt","quincunx", "convex"])) assert(is_consistent(points), "Non-rectangular or invalid point array") let( pts = flatten(points), pcnt = len(pts), rows = len(points), - cols = len(points[0]), + cols = len(points[0]) + ) + rows<=1 || cols<=1 ? vnf : + let( cap1 = first_defined([cap1,caps,false]), cap2 = first_defined([cap2,caps,false]), colcnt = cols - (col_wrap?0:1), rowcnt = rows - (row_wrap?0:1), verts = [ each pts, - if (style=="quincunx") ( - for (r = [0:1:rowcnt-1]) ( - for (c = [0:1:colcnt-1]) ( - let( - i1 = ((r+0)%rows)*cols + ((c+0)%cols), - i2 = ((r+1)%rows)*cols + ((c+0)%cols), - i3 = ((r+1)%rows)*cols + ((c+1)%cols), - i4 = ((r+0)%rows)*cols + ((c+1)%cols) - ) mean([pts[i1], pts[i2], pts[i3], pts[i4]]) - ) - ) - ) + if (style=="quincunx") + for (r = [0:1:rowcnt-1], c = [0:1:colcnt-1]) + let( + i1 = ((r+0)%rows)*cols + ((c+0)%cols), + i2 = ((r+1)%rows)*cols + ((c+0)%cols), + i3 = ((r+1)%rows)*cols + ((c+1)%cols), + i4 = ((r+0)%rows)*cols + ((c+1)%cols) + ) + mean([pts[i1], pts[i2], pts[i3], pts[i4]]) ] ) - rows<=1 || cols<=1 ? vnf : vnf_merge(cleanup=true, [ vnf, [ - verts, - concat( - [ - for (r = [0:1:rowcnt-1]) ( - for (c = [0:1:colcnt-1]) each ( - let( - i1 = ((r+0)%rows)*cols + ((c+0)%cols), - i2 = ((r+1)%rows)*cols + ((c+0)%cols), - i3 = ((r+1)%rows)*cols + ((c+1)%cols), - i4 = ((r+0)%rows)*cols + ((c+1)%cols), - faces = style=="quincunx"? ( - let(i5 = pcnt + r*colcnt + c) - [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]] - ) : style=="alt"? ( - [[i1,i4,i2],[i2,i4,i3]] - ) : style=="convex"? let( - fsets = [ - [[i1,i4,i2],[i2,i4,i3]], - [[i1,i3,i2],[i1,i4,i3]] - ], - cps = [for (fset=fsets) [for (f=fset) mean(select(pts,f))]], - ns = cps + [for (fset=fsets) [for (f=fset) polygon_normal(select(pts,f))]], - dists = [for (i=idx(fsets)) norm(cps[i][1]-cps[i][0]) - norm(ns[i][1]-ns[i][0])], - test = reverse? dists[0]>dists[1] : dists[0]=3) face] - ) faces - ) - ) - ], - !cap1? [] : [ - reverse? - [for (c = [0:1:cols-1]) c] : - [for (c = [cols-1:-1:0]) c] - ], - !cap2? [] : [ - reverse? - [for (c = [cols-1:-1:0]) (rows-1)*cols + c] : - [for (c = [0:1:cols-1]) (rows-1)*cols + c] - ] - ) - ] + verts, + [ + for (r = [0:1:rowcnt-1], c=[0:1:colcnt-1]) + each + let( + i1 = ((r+0)%rows)*cols + ((c+0)%cols), + i2 = ((r+1)%rows)*cols + ((c+0)%cols), + i3 = ((r+1)%rows)*cols + ((c+1)%cols), + i4 = ((r+0)%rows)*cols + ((c+1)%cols), + faces = + style=="quincunx"? + let(i5 = pcnt + r*colcnt + c) + [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]] + : style=="alt"? + [[i1,i4,i2],[i2,i4,i3]] + : style=="convex"? + let( + fsets = [ + [[i1,i4,i2],[i2,i4,i3]], + [[i1,i3,i2],[i1,i4,i3]] + ], + cps = [for (fset=fsets) [for (f=fset) mean(select(pts,f))]], + ns = cps + [for (fset=fsets) [for (f=fset) polygon_normal(select(pts,f))]], + dists = [for (i=idx(fsets)) norm(cps[i][1]-cps[i][0]) - norm(ns[i][1]-ns[i][0])], + test = reverse? dists[0]>dists[1] : dists[0]=3) dface + ] + ) + dfaces, + if (cap1) count(cols,reverse=!reverse), + if (cap2) count(cols,(rows-1)*cols, reverse=reverse) + ] + ] ]); @@ -435,7 +429,7 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) = for(j=[0:1:count]) reverse ? [j+rowstart, j+nextrow, j+nextrow+1] : [j+rowstart, j+nextrow+1, j+nextrow], // bot triangles left for(j=[count+1:1:select(lens,i+1)-2]) reverse ? [j+rowstart-1, j+nextrow, j+nextrow+1] : [j+rowstart-1, j+nextrow+1, j+nextrow], // bot triangles right ] : - delta == -2 ? + delta == -2 ? [ for(j=[0:1:count-2]) reverse ? [j+nextrow, j+nextrow+1, j+rowstart+1] : [j+nextrow, j+rowstart+1, j+nextrow+1], for(j=[count-1:1:lens[i]-4]) reverse ? [j+nextrow,j+nextrow+1,j+rowstart+2] : [j+nextrow,j+rowstart+2, j+nextrow+1], From 4a179920cf11a1c2db73cb8196321d0c3e9a4574 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 13 Apr 2021 19:27:42 -0400 Subject: [PATCH 2/5] Speed improvement for vnf_vertex_array by changing deduplicate testing. Add "min_edge" style to vnf vertex array. eliminate _skin_core, replace with vnf_vertex_array, add style option to all modules/functions. fix bug in path_normals (not normalized) --- paths.scad | 2 +- skin.scad | 115 ++++++++++++++++------------------------------------- vnf.scad | 41 +++++++++++++------ 3 files changed, 64 insertions(+), 94 deletions(-) diff --git a/paths.scad b/paths.scad index 74f999e..e178e74 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/skin.scad b/skin.scad index cf75796..446a938 100644 --- a/skin.scad +++ b/skin.scad @@ -13,9 +13,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 +152,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 +377,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); 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 +389,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]) @@ -483,61 +484,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close ) each subdivide_and_slice(pair,slices[i], nsamples, method=sampling)] ) - _skin_core(full_list, caps=fullcaps); - - - -function _skin_core(profiles, caps) = - let( - vertices = [for (prof=profiles) each prof], - plens = [for (prof=profiles) len(prof)], - sidefaces = [ - 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), - faces = [ - for( - first = true, - finishing = false, - finished = false, - plen1 = len(prof1), - plen2 = len(prof2), - i=0, j=0, side=0; - - !finished; - - side = - let( - p1a = prof1[(i+0)%plen1], - p1b = prof1[(i+1)%plen1], - p2a = prof2[(j+0)%plen2], - p2b = prof2[(j+1)%plen2], - dist1 = norm(p1a-p2b), - dist2 = norm(p1b-p2a) - ) (i==j) ? (dist1>dist2? 1 : 0) : (i=plen1 && j>=plen2 - ) 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)]; + vnf_vertex_array(full_list, caps=fullcaps, col_wrap=true, style=style); @@ -903,9 +850,9 @@ function associate_vertices(polygons, split, curpoly=0) = // Function&Module: sweep() // Usage: As Module -// sweep(shape, transforms, , , , , , , ) ; +// sweep(shape, transforms, , ,