diff --git a/beziers.scad b/beziers.scad index 4cc5ee8..f4b034f 100644 --- a/beziers.scad +++ b/beziers.scad @@ -1124,7 +1124,7 @@ function is_patch(x) = // Function: bezier_patch() // Usage: -// vnf = bezier_patch(patch, [splinesteps], [vnf=], [style=]); +// vnf = bezier_patch(patch, [splinesteps], [style=]); // Topics: Bezier Patches // See Also: bezier_points(), bezier_curve(), bezier_path(), bezier_patch_points(), bezier_triangle_point() // Description: @@ -1137,7 +1137,6 @@ function is_patch(x) = // patch = The rectangular or triangular array of endpoints and control points for this bezier patch. // splinesteps = Number of steps to divide each bezier segment into. For rectangular patches you can specify [XSTEPS,YSTEPS]. Default: 16 // --- -// vnf = Vertices'n'Faces [VNF structure](vnf.scad) to add new vertices and faces to. Default: empty VNF // style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx". // Example(3D): // patch = [ @@ -1158,7 +1157,7 @@ function is_patch(x) = // ]; // vnf = bezier_patch(tri, splinesteps=16); // vnf_polyhedron(vnf); -// Example(3D,FlatSpin,VPD=444): Chaining Patches +// Example(3D,FlatSpin,VPD=444): Merging multiple patches // patch = [ // // u=0,v=0 u=1,v=0 // [[0, 0,0], [33, 0, 0], [67, 0, 0], [100, 0,0]], @@ -1167,13 +1166,15 @@ function is_patch(x) = // [[0,100,0], [33,100, 0], [67,100, 0], [100,100,0]], // // u=0,v=1 u=1,v=1 // ]; -// vnf1 = bezier_patch(translate(p=patch,[-50,-50,50])); -// vnf2 = bezier_patch(vnf=vnf1, rot(a=[90,0,0],p=translate(p=patch,[-50,-50,50]))); -// vnf3 = bezier_patch(vnf=vnf2, rot(a=[-90,0,0],p=translate(p=patch,[-50,-50,50]))); -// vnf4 = bezier_patch(vnf=vnf3, rot(a=[180,0,0],p=translate(p=patch,[-50,-50,50]))); -// vnf5 = bezier_patch(vnf=vnf4, rot(a=[0,90,0],p=translate(p=patch,[-50,-50,50]))); -// vnf6 = bezier_patch(vnf=vnf5, rot(a=[0,-90,0],p=translate(p=patch,[-50,-50,50]))); -// vnf_polyhedron(vnf6); +// tpatch = translate([-50,-50,50], patch); +// vnf = vnf_merge([ +// bezier_patch(tpatch), +// bezier_patch(xrot(90, tpatch)), +// bezier_patch(xrot(-90, tpatch)), +// bezier_patch(xrot(180, tpatch)), +// bezier_patch(yrot(90, tpatch)), +// bezier_patch(yrot(-90, tpatch))]); +// vnf_polyhedron(vnf); // Example(3D): Connecting Patches with Asymmetric Splinesteps // steps = 8; // edge_patch = [ @@ -1223,10 +1224,10 @@ function is_patch(x) = // ) // ]; // vnf_polyhedron(concat(edges,corners,faces)); -function bezier_patch(patch, splinesteps=16, vnf=EMPTY_VNF, style="default") = +function bezier_patch(patch, splinesteps=16, style="default") = assert(is_num(splinesteps) || is_vector(splinesteps,2)) assert(all_positive(splinesteps)) - is_tripatch(patch)? _bezier_triangle(patch, splinesteps=splinesteps, vnf=vnf) : + is_tripatch(patch)? _bezier_triangle(patch, splinesteps=splinesteps) : let( splinesteps = is_list(splinesteps) ? splinesteps : [splinesteps,splinesteps], uvals = [ @@ -1238,7 +1239,7 @@ function bezier_patch(patch, splinesteps=16, vnf=EMPTY_VNF, style="default") = 1-step/splinesteps.y ], pts = bezier_patch_points(patch, uvals, vvals), - vnf = vnf_vertex_array(pts, style=style, vnf=vnf, reverse=false) + vnf = vnf_vertex_array(pts, style=style, reverse=false) ) vnf; @@ -1431,7 +1432,7 @@ function bezier_patch_degenerate(patch, splinesteps=16, reverse=false, return_ed function _tri_count(n) = (n*(1+n))/2; -function _bezier_triangle(tri, splinesteps=16, vnf=EMPTY_VNF) = +function _bezier_triangle(tri, splinesteps=16) = assert(is_num(splinesteps)) let( pts = [ @@ -1456,7 +1457,7 @@ function _bezier_triangle(tri, splinesteps=16, vnf=EMPTY_VNF) = ) ) for (face=allfaces) face ] - ) vnf_merge([vnf,[pts, faces]]); + ) [pts, faces]; @@ -1507,7 +1508,7 @@ function patch_reverse(patch) = // Function: bezier_surface() // Usage: -// vnf = bezier_surface(patches, [splinesteps], [vnf=], [style=]); +// vnf = bezier_surface(patches, [splinesteps], [style]); // Topics: Bezier Patches // See Also: bezier_patch_points(), bezier_patch_flat() // Description: @@ -1520,8 +1521,6 @@ function patch_reverse(patch) = // Arguments: // patches = A list of triangular and/or rectangular bezier patches. // splinesteps = Number of steps to divide each bezier segment into. Default: 16 -// --- -// vnf = Vertices'n'Faces [VNF structure](vnf.scad) to add new vertices and faces to. Default: empty VNF // style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx". // Example(3D): // patch1 = [ @@ -1538,13 +1537,8 @@ function patch_reverse(patch) = // ]; // vnf = bezier_surface(patches=[patch1, patch2], splinesteps=16); // polyhedron(points=vnf[0], faces=vnf[1]); -function bezier_surface(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="default", i=0) = - let( - vnf = (i >= len(patches))? vnf : - bezier_patch(patches[i], splinesteps=splinesteps, vnf=vnf, style=style) - ) (i >= len(patches))? vnf : - bezier_surface(patches=patches, splinesteps=splinesteps, vnf=vnf, style=style, i=i+1); - +function bezier_surface(patches=[], splinesteps=16, style="default") = + vnf_merge([for(patch=patches) bezier_patch(patch, splinesteps=splinesteps, style=style)]); // Module: trace_bezier_patches() diff --git a/tests/test_skin.scad b/tests/test_skin.scad index ec0fc52..405e19b 100644 --- a/tests/test_skin.scad +++ b/tests/test_skin.scad @@ -11,7 +11,7 @@ module test_skin() { assert_equal(vnf1, [[[-100,-100,0],[0,100,0],[0,100,0],[100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[0,5,4],[0,1,5],[5,2,6],[2,3,6],[6,3,7],[3,0,7],[7,0,4]]]); vnf2 = skin(profiles, slices=0, caps=true, method="distance"); - assert_equal(vnf2,[[[-100,-100,0],[0,100,0],[0,100,0],[100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[0,5,4],[0,1,5],[5,2,6],[2,3,6],[6,3,7],[3,0,7],[7,0,4],[3,2,1,0],[4,5,6,7]]]); + assert_equal(vnf2,[[[-100,-100,0],[0,100,0],[0,100,0],[100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[3,2,1,0],[4,5,6,7],[0,5,4],[0,1,5],[5,2,6],[2,3,6],[6,3,7],[3,0,7],[7,0,4]]]); } test_skin(); diff --git a/tests/test_vnf.scad b/tests/test_vnf.scad index e793ff7..3c65c26 100644 --- a/tests/test_vnf.scad +++ b/tests/test_vnf.scad @@ -98,9 +98,9 @@ module test_vnf_vertex_array() { points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], col_wrap=true, caps=true, style="quincunx" ); - assert(vnf1 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,4,3],[0,1,4],[1,5,4],[1,2,5],[2,3,5],[2,0,3],[2,1,0],[3,4,5]]]); - assert(vnf2 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,1,3],[3,1,4],[1,2,4],[4,2,5],[2,0,5],[5,0,3],[2,1,0],[3,4,5]]]); - assert(vnf3 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100],[0,-50,50],[-50,25,50],[50,25,50]],[[0,6,3],[3,6,4],[4,6,1],[1,6,0],[1,7,4],[4,7,5],[5,7,2],[2,7,1],[2,8,5],[5,8,3],[3,8,0],[0,8,2],[2,1,0],[3,4,5]]]); + assert(vnf1 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[2,1,0],[3,4,5],[0,4,3],[0,1,4],[1,5,4],[1,2,5],[2,3,5],[2,0,3]]]); + assert(vnf2 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[2,1,0],[3,4,5],[0,1,3],[3,1,4],[1,2,4],[4,2,5],[2,0,5],[5,0,3]]]); + assert(vnf3 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100],[0,-50,50],[-50,25,50],[50,25,50]],[[2,1,0],[3,4,5],[0,6,3],[3,6,4],[4,6,1],[1,6,0],[1,7,4],[4,7,5],[5,7,2],[2,7,1],[2,8,5],[5,8,3],[3,8,0],[0,8,2]]]); } test_vnf_vertex_array(); diff --git a/vnf.scad b/vnf.scad index ec94018..f6a27c0 100644 --- a/vnf.scad +++ b/vnf.scad @@ -16,17 +16,17 @@ // You can construct a `polyhedron()` in parts by describing each part in a self-contained VNF, then // merge the various VNFs to get the completed polyhedron vertex list and faces. -// Constant: EMPTY_VNF -// Description: -// The empty VNF data structure. Equal to `[[],[]]`. +/// Constant: EMPTY_VNF +/// Description: +/// The empty VNF data structure. Equal to `[[],[]]`. EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Function: vnf_vertex_array() // Usage: -// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap], [vnf]); +// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap]); // Description: -// Creates a VNF structure from a vertex list, by dividing the vertices into columns and rows, +// Creates a VNF structure from a rectangular vertex list, by dividing the vertices into columns and rows, // adding faces to tile the surface. You can optionally have faces added to wrap the last column // back to the first column, or wrap the last row to the first. Endcaps can be added to either // the first and/or last rows. The style parameter determines how the quadrilaterals are divided into @@ -34,17 +34,18 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // is the uniform subdivision in the other (alternate) direction. The "min_edge" style picks the shorter edge to // subdivide for each quadrilateral, so the division may not be uniform across the shape. The "quincunx" style // adds a vertex in the center of each quadrilateral and creates four triangles, and the "convex" and "concave" styles -// chooses the locally convex/concave subdivision. +// chooses the locally convex/concave subdivision. Degenerate faces +// are not included in the output, but if this results in unused vertices they will still appear in the output. // Arguments: // points = A list of vertices to divide into columns and rows. +// --- // caps = If true, add endcap faces to the first AND last rows. // cap1 = If true, add an endcap face to the first row. // cap2 = If true, add an endcap face to the last row. // col_wrap = If true, add faces to connect the last column to the first. // row_wrap = If true, add faces to connect the last row to the first. // reverse = If true, reverse all face normals. -// style = The style of subdividing the quads into faces. Valid options are "default", "alt", "min_edge", "quincunx","convex" and "concave". -// vnf = If given, add all the vertices and faces to this existing VNF structure. +// style = The style of subdividing the quads into faces. Valid options are "default", "alt", "min_edge", "quincunx", "convex" and "concave". // Example(3D): // vnf = vnf_vertex_array( // points=[ @@ -104,12 +105,12 @@ function vnf_vertex_array( col_wrap=false, row_wrap=false, reverse=false, - style="default", - vnf=EMPTY_VNF + style="default" ) = 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","concave", "min_edge"])) + assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d") assert(is_consistent(points), "Non-rectangular or invalid point array") let( pts = flatten(points), @@ -117,7 +118,7 @@ function vnf_vertex_array( rows = len(points), cols = len(points[0]) ) - rows<=1 || cols<=1 ? vnf : + rows<=1 || cols<=1 ? EMPTY_VNF : let( cap1 = first_defined([cap1,caps,false]), cap2 = first_defined([cap2,caps,false]), @@ -134,66 +135,61 @@ function vnf_vertex_array( i4 = ((r+0)%rows)*cols + ((c+1)%cols) ) mean([pts[i1], pts[i2], pts[i3], pts[i4]]) + ], + allfaces = [ + if (cap1) count(cols,reverse=!reverse), + if (cap2) count(cols,(rows-1)*cols, reverse=reverse), + 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=="min_edge"? + let( + d42=norm(pts[i4]-pts[i2]), + d13=norm(pts[i1]-pts[i3]), + shortedge = d42<=d13 ? [[i1,i4,i2],[i2,i4,i3]] + : [[i1,i3,i2],[i1,i4,i3]] + ) + shortedge + : style=="convex"? + let( // Find normal for 3 of the points. Is the other point above or below? + n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), + convexfaces = n==0 ? [[i1,i4,i3]] + : n*pts[i4] > n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] + : [[i1,i3,i2],[i1,i4,i3]] + ) + convexfaces + : style=="concave"? + let( // Find normal for 3 of the points. Is the other point above or below? + n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), + concavefaces = n==0 ? [[i1,i4,i3]] + : n*pts[i4] <= n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] + : [[i1,i3,i2],[i1,i4,i3]] + ) + concavefaces + : [[i1,i3,i2],[i1,i4,i3]], + // remove degenerate faces + culled_faces= [for(face=faces) + if (norm(verts[face[0]]-verts[face[1]])>EPSILON && + norm(verts[face[1]]-verts[face[2]])>EPSILON && + norm(verts[face[2]]-verts[face[0]])>EPSILON) + face + ], + rfaces = reverse? [for (face=culled_faces) reverse(face)] : culled_faces + ) + rfaces, ] ) - vnf_merge(cleanup=false, [ - vnf, - [ - 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=="min_edge"? - let( - d42=norm(pts[i4]-pts[i2]), - d13=norm(pts[i1]-pts[i3]), - shortedge = d42<=d13 ? [[i1,i4,i2],[i2,i4,i3]] - : [[i1,i3,i2],[i1,i4,i3]] - ) - shortedge - : style=="convex"? - let( // Find normal for 3 of the points. Is the other point above or below? - n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), - convexfaces = n==0 ? [[i1,i4,i3]] - : n*pts[i4] > n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] - : [[i1,i3,i2],[i1,i4,i3]] - ) - convexfaces - : style=="concave"? - let( // Find normal for 3 of the points. Is the other point above or below? - n = (reverse?-1:1)*cross(pts[i2]-pts[i1],pts[i3]-pts[i1]), - concavefaces = n==0 ? [[i1,i4,i3]] - : n*pts[i4] <= n*pts[i1] ? [[i1,i4,i2],[i2,i4,i3]] - : [[i1,i3,i2],[i1,i4,i3]] - ) - concavefaces - : [[i1,i3,i2],[i1,i4,i3]], - // remove degenerate faces - culled_faces= [for(face=faces) - if (norm(verts[face[0]]-verts[face[1]])>EPSILON && - norm(verts[face[1]]-verts[face[2]])>EPSILON && - norm(verts[face[2]]-verts[face[0]])>EPSILON) - face - ], - rfaces = reverse? [for (face=culled_faces) reverse(face)] : culled_faces - ) - rfaces, - if (cap1) count(cols,reverse=!reverse), - if (cap2) count(cols,(rows-1)*cols, reverse=reverse) - ] - ] - ]); + [verts,allfaces]; // Function: vnf_tri_array() @@ -202,7 +198,8 @@ function vnf_vertex_array( // Description: // Produces a vnf from an array of points where each row length can differ from the adjacent rows by up to 2 in length. This enables // the construction of triangular VNF patches. The resulting VNF can be wrapped along the rows by setting `row_wrap` to true. -// You cannot wrap columns: if you need to do that you'll need to combine two VNF arrays that share edges. +// You cannot wrap columns: if you need to do that you'll need to merge two VNF arrays that share edges. Degenerate faces +// are not included in the output, but if this results in unused vertices they will still appear in the output. // Arguments: // points = List of point lists for each row // row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length. @@ -217,18 +214,18 @@ function vnf_vertex_array( // vnf = vnf_tri_array(pts); // vnf_wireframe(vnf,width=0.1); // color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9); -// Example(3D): Chaining two VNFs to construct a cone with one point length change between rows. +// Example(3D): Merging two VNFs to construct a cone with one point length change between rows. // pts1 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[0,180]),10-z)]; // pts2 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[180,360]),10-z)]; -// vnf = vnf_tri_array(pts1, -// vnf=vnf_tri_array(pts2)); +// vnf = vnf_merge([vnf_tri_array(pts1), +// vnf_tri_array(pts2)]); // color("green")vnf_wireframe(vnf,width=0.1); // vnf_polyhedron(vnf); // Example(3D): Cone with length change two between rows // pts1 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[0,180]),10-z)]; // pts2 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[180,360]),10-z)]; -// vnf = vnf_tri_array(pts1, -// vnf=vnf_tri_array(pts2)); +// vnf = vnf_merge([vnf_tri_array(pts1), +// vnf_tri_array(pts2)]); // color("green")vnf_wireframe(vnf,width=0.1); // vnf_polyhedron(vnf); // Example(3D,NoAxes): Point count can change irregularly @@ -237,8 +234,8 @@ function vnf_vertex_array( // vnf = vnf_tri_array(pts); // vnf_wireframe(vnf,width=0.1); // color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9); -function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) = - let( +function vnf_tri_array(points, row_wrap=false, reverse=false) = + let( lens = [for(row=points) len(row)], rowstarts = [0,each cumsum(lens)], faces = @@ -273,8 +270,17 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) = for(j=[count:1:select(lens,i+1)]) reverse ? [ j+nextrow-1, j+rowstart+1, j+rowstart]: [ j+nextrow-1, j+rowstart, j+rowstart+1], ] : assert(false,str("Unsupported row length difference of ",delta, " between row ",i," and ",(i+1)%len(points))) - ]) - vnf_merge(cleanup=true, [vnf, [flatten(points), faces]]); + ], + verts = flatten(points), + culled_faces= + [for(face=faces) + if (norm(verts[face[0]]-verts[face[1]])>EPSILON && + norm(verts[face[1]]-verts[face[2]])>EPSILON && + norm(verts[face[2]]-verts[face[0]])>EPSILON) + face + ] + ) + [flatten(points), culled_faces]; @@ -292,7 +298,9 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) = // eps = the tolerance in finding duplicates when cleanup=true. Default: EPSILON function vnf_merge(vnfs, cleanup=false, eps=EPSILON) = is_vnf(vnfs) ? vnf_merge([vnfs], cleanup, eps) : - assert( is_vnf_list(vnfs) , "Improper vnf or vnf list")   + assert( is_vnf_list(vnfs) , "Improper vnf or vnf list") + len(vnfs)==1 ? (cleanup ? _vnf_cleanup(vnfs[0][0],vnfs[0][1],eps) : vnfs[0]) + : let ( offs = cumsum([ 0, for (vnf = vnfs) len(vnf[0]) ]), verts = [for (vnf=vnfs) each vnf[0]], @@ -307,7 +315,11 @@ function vnf_merge(vnfs, cleanup=false, eps=EPSILON) = offs[i] + j ] ] ) - ! cleanup ? [verts, faces] : + cleanup? _vnf_cleanup(verts,faces,eps) : [verts,faces]; + + + +function _vnf_cleanup(verts,faces,eps) = let( dedup = vector_search(verts,eps,verts), // collect vertex duplicates map = [for(i=idx(verts)) min(dedup[i]) ], // remap duplic vertices @@ -405,7 +417,7 @@ function _cleave_connected_region(region) = // Function: vnf_from_region() // Usage: -// vnf = vnf_from_region(region, [transform], [reverse], [vnf]); +// vnf = vnf_from_region(region, [transform], [reverse]); // Description: // Given a (two-dimensional) region, applies the given transformation matrix to it and makes a (three-dimensional) triangulated VNF of // faces for that region, reversed if desired. @@ -413,7 +425,6 @@ function _cleave_connected_region(region) = // region = The region to conver to a vnf. // transform = If given, a transformation matrix to apply to the faces generated from the region. Default: No transformation applied. // reverse = If true, reverse the normals of the faces generated from the region. An untransformed region will have face normals pointing `UP`. Default: false -// vnf = If given, the faces are added to this VNF. Default: `EMPTY_VNF` // Example(3D): // region = [square([20,10],center=true), // right(5,square(4,center=true)), @@ -422,11 +433,10 @@ function _cleave_connected_region(region) = // color("gray")down(.125) // linear_extrude(height=.125)region(region); // vnf_wireframe(vnf,width=.25); -function vnf_from_region(region, transform, reverse=false, vnf=EMPTY_VNF) = +function vnf_from_region(region, transform, reverse=false) = let ( regions = region_parts(force_region(region)), vnfs = [ - if (vnf != EMPTY_VNF) vnf, for (rgn = regions) let( cleaved = path3d(_cleave_connected_region(rgn)), face = is_undef(transform)? cleaved : apply(transform,cleaved), @@ -684,11 +694,11 @@ module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin" // Module: vnf_wireframe() // Usage: -// vnf_wireframe(vnf, ); +// vnf_wireframe(vnf, [width]); // Description: // Given a VNF, creates a wire frame ball-and-stick model of the polyhedron with a cylinder for // each edge and a sphere at each vertex. The width parameter specifies the width of the sticks -// that form the wire frame. +// that form the wire frame and the diameter of the balls. // Arguments: // vnf = A vnf structure // width = width of the cylinders forming the wire frame. Default: 1 @@ -930,6 +940,7 @@ function _triangulate_planar_convex_polygons(polys) = // Arguments: // vnf = The original VNF to bend. // r = If given, the radius where the size of the original shape is the same as in the original. +// --- // d = If given, the diameter where the size of the original shape is the same as in the original. // axis = The axis to wrap around. "X", "Y", or "Z". Default: "Z" // Example(3D):