diff --git a/beziers.scad b/beziers.scad index cb6a89d..00ba0e6 100644 --- a/beziers.scad +++ b/beziers.scad @@ -241,9 +241,9 @@ function bezier_triangle_point(patch, u, v) = len(patch) == 1 ? patch[0][0] : let( n = len(patch)-1, - Pu = [for(i=[0:n-1]) select(patch[i],1,-1)], - Pv = [for(i=[0:n-1]) select(patch[i],0,-2)], - Pw = select(patch,1,-1) + Pu = [for(i=[0:n-1]) [for (j=[1:len(patch[i])-1]) patch[i][j]]], + Pv = [for(i=[0:n-1]) [for (j=[0:len(patch[i])-2]) patch[i][j]]], + Pw = [for(i=[1:len(patch)-1]) patch[i]] ) bezier_triangle_point(u*Pu + v*Pv + (1-u-v)*Pw, u, v); @@ -254,9 +254,9 @@ function _vertex_list_merge(v1, v2) = concat(v1, [for (v=v2) if (!in_list(v,v1)) function _vertex_list_face(v, face) = [for (pt = face) search([pt], v, num_returns_per_match=1)[0]]; -// Function: bezier_patch_vertices_and_faces() +// Function: bezier_patch() // Usage: -// bezier_patch_vertices_and_faces(patch, [splinesteps], [vertices], [faces]); +// bezier_patch(patch, [splinesteps], [vertices], [faces]); // Description: // Calculate vertices and faces for forming a partial polyhedron // from the given bezier rectangular patch. Returns a list containing @@ -270,30 +270,33 @@ function _vertex_list_face(v, face) = [for (pt = face) search([pt], v, num_retur // splinesteps = Number of steps to divide each bezier segment into. Default: 16 // vertices = Vertex list to add new points to. Default: [] // faces = Face list to add new faces to. Default: [] -function bezier_patch_vertices_and_faces(patch, splinesteps=16, vertices=[], faces=[]) = +function bezier_patch(patch, splinesteps=16, vertices=[], faces=[]) = let( + base = len(vertices), pts = [for (v=[0:splinesteps], u=[0:splinesteps]) bezier_patch_point(patch, u/splinesteps, v/splinesteps)], - new_vertices = _vertex_list_merge(vertices, pts), + new_vertices = concat(vertices, pts), new_faces = [ for ( v=[0:splinesteps-1], u=[0:splinesteps-1], i=[0,1] ) let ( - v1 = u+v*(splinesteps+1), + v1 = u+v*(splinesteps+1) + base, v2 = v1 + 1, v3 = v1 + splinesteps + 1, v4 = v3 + 1, - face = i? [v1,v3,v2] : [v2,v3,v4], - facepts = [for (idx = face) pts[idx]] - ) _vertex_list_face(new_vertices, facepts) + face = i? [v1,v3,v2] : [v2,v3,v4] + ) face ] ) [new_vertices, concat(faces, new_faces)]; -// Function: bezier_triangle_vertices_and_faces() +function _tri_count(n) = (n*(1+n))/2; + + +// Function: bezier_triangle() // Usage: -// bezier_triangle_vertices_and_faces(patch, [splinesteps], [vertices], [faces]); +// bezier_triangle(patch, [splinesteps], [vertices], [faces]); // Description: // Calculate vertices and faces for forming a partial polyhedron // from the given bezier triangular patch. Returns a list containing @@ -307,24 +310,40 @@ function bezier_patch_vertices_and_faces(patch, splinesteps=16, vertices=[], fac // splinesteps = Number of steps to divide each bezier segment into. Default: 16 // vertices = Vertex list to add new points to. Default: [] // faces = Face list to add new faces to. Default: [] -function bezier_triangle_vertices_and_faces(patch, splinesteps=16, vertices=[], faces=[]) = +// Example(3D): +// tri = [ +// [[-50,-33,0], [-25,16,-50], [0,66,0]], +// [[0,-33,-50], [25,16,-50]], +// [[50,-33,0]] +// ]; +// vnf = bezier_triangle(tri, splinesteps=16); +// polyhedron(points=vnf[0], faces=vnf[1]); +function bezier_triangle(patch, splinesteps=16, vertices=[], faces=[]) = let( - pts = [for (u=[0:splinesteps], v=[0:splinesteps-u]) bezier_triangle_point(patch, u/splinesteps, v/splinesteps)], - new_vertices = _vertex_list_merge(vertices, pts), + base = len(vertices), + pts = [ + for ( + u=[0:splinesteps], + v=[0:splinesteps-u] + ) bezier_triangle_point(patch, u/splinesteps, v/splinesteps) + ], + new_vertices = concat(vertices, pts), + patchlen = len(patch), + tricnt = _tri_count(splinesteps+1), new_faces = [ for ( u=[0:splinesteps-1], v=[0:splinesteps-u-1] ) let ( - v1 = bezier_triangle_point(patch, u/splinesteps, v/splinesteps), - v2 = bezier_triangle_point(patch, (u+1)/splinesteps, v/splinesteps), - v3 = bezier_triangle_point(patch, u/splinesteps, (v+1)/splinesteps), - v4 = bezier_triangle_point(patch, (u+1)/splinesteps, (v+1)/splinesteps), + v1 = v + (tricnt - _tri_count(splinesteps+1-u)) + base, + v2 = v1 + 1, + v3 = v + (tricnt - _tri_count(splinesteps-u)) + base, + v4 = v3 + 1, allfaces = concat( [[v1,v2,v3]], ((u= len(patches))? [vertices, faces] : - bezier_patch_vertices_and_faces(patches[i], splinesteps=splinesteps, vertices=vertices, faces=faces), - vnf2 = (i >= len(tripatches))? vnf : - bezier_triangle_vertices_and_faces(tripatches[i], splinesteps=splinesteps, vertices=vnf[0], faces=vnf[1]) - ) (i >= len(patches) && i >= len(tripatches))? vnf2 : - bezier_surface_vertices_and_faces(patches=patches, tripatches=tripatches, splinesteps=splinesteps, i=i+1, vertices=vnf2[0], faces=vnf2[1]); + bezier_patch(patches[i], splinesteps=splinesteps, vertices=vertices, faces=faces), + vnf2 = (i >= len(tris))? vnf : + bezier_triangle(tris[i], splinesteps=splinesteps, vertices=vnf[0], faces=vnf[1]) + ) (i >= len(patches) && i >= len(tris))? vnf2 : + bezier_surface_vertices_and_faces(patches=patches, tris=tris, splinesteps=splinesteps, i=i+1, vertices=vnf2[0], faces=vnf2[1]); @@ -926,7 +945,7 @@ module trace_bezier(bez, N=3, size=1) { // Takes a list of two or more bezier patches and attempts to make a complete polyhedron from them. // Arguments: // patches = A list of rectangular bezier patches. -// tripatches = A list of triangular bezier patches. +// tris = A list of triangular bezier patches. // vertices = Vertex list for additional non-bezier faces. Default: [] // faces = Additional non-bezier faces. Default: [] // splinesteps = Number of steps to divide each bezier segment into. Default: 16 @@ -944,9 +963,9 @@ module trace_bezier(bez, N=3, size=1) { // [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]], // ]; // bezier_polyhedron([patch1, patch2], splinesteps=8); -module bezier_polyhedron(patches=[], tripatches=[], splinesteps=16, vertices=[], faces=[]) +module bezier_polyhedron(patches=[], tris=[], splinesteps=16, vertices=[], faces=[]) { - sfc = bezier_surface_vertices_and_faces(patches=patches, tripatches=tripatches, splinesteps=splinesteps, vertices=vertices, faces=faces); + sfc = bezier_surface_vertices_and_faces(patches=patches, tris=tris, splinesteps=splinesteps, vertices=vertices, faces=faces); polyhedron(points=sfc[0], faces=sfc[1]); } @@ -955,13 +974,13 @@ module bezier_polyhedron(patches=[], tripatches=[], splinesteps=16, vertices=[], // Module: trace_bezier_patches() // Usage: // trace_bezier_patches(patches, [size], [showcps], [splinesteps]); -// trace_bezier_patches(tripatches, [size], [showcps], [splinesteps]); -// trace_bezier_patches(patches, tripatches, [size], [showcps], [splinesteps]); +// trace_bezier_patches(tris, [size], [showcps], [splinesteps]); +// trace_bezier_patches(patches, tris, [size], [showcps], [splinesteps]); // Description: // Shows the surface, and optionally, control points of a list of bezier patches. // Arguments: // patches = A list of rectangular bezier patches. -// tripatches = A list of triangular bezier patches. +// tris = A list of triangular bezier patches. // splinesteps = Number of steps to divide each bezier segment into. default=16 // showcps = If true, show the controlpoints as well as the surface. // size = Size to show control points and lines. @@ -979,7 +998,7 @@ module bezier_polyhedron(patches=[], tripatches=[], splinesteps=16, vertices=[], // [[15,85,0], [33,100, 0], [ 67,100, 0], [ 85, 85,0]], // ]; // trace_bezier_patches(patches=[patch1, patch2], splinesteps=8, showcps=true); -module trace_bezier_patches(patches=[], tripatches=[], size=1, showcps=false, splinesteps=16) +module trace_bezier_patches(patches=[], tris=[], size=1, showcps=false, splinesteps=16) { if (showcps) { for (patch = patches) { @@ -989,9 +1008,10 @@ module trace_bezier_patches(patches=[], tripatches=[], size=1, showcps=false, sp if (i +use +use +use + + +function CR_corner(size, orient=[0,0,0], trans=[0,0,0]) = + let ( + r = 0.4, + k = r/2, + // I know this patch is not yet correct for continuous + // rounding, but it's a first approximation proof of concept. + // Currently this is a degree 4 triangular patch. + patch = [ + [[1,1,0], [1,r,0], [1,0,0], [1,0,r], [1,0,1]], + [[r,1,0], [k,k,0], [k,0,k], [r,0,1]], + [[0,1,0], [0,k,k], [0,0,1]], + [[0,1,r], [0,r,1]], + [[0,1,1]] + ] + ) [for (row=patch) + translate_points(v=trans, + rotate_points3d(v=orient, + scale_points(v=size, row) + ) + ) + ]; + + +function CR_edge(size, orient=[0,0,0], trans=[0,0,0]) = + let ( + r = 0.4, + a = -1/2, + b = -1/4, + c = 1/4, + d = 1/2, + // I know this patch is not yet correct for continuous + // rounding, but it's a first approximation proof of concept. + // Currently this is a degree 4 rectangular patch. + patch = [ + [[1,0,a], [1,0,b], [1,0,0], [1,0,c], [1,0,d]], + [[r,0,a], [r,0,b], [r,0,0], [r,0,c], [r,0,d]], + [[0,0,a], [0,0,b], [0,0,0], [0,0,c], [0,0,d]], + [[0,r,a], [0,r,b], [0,r,0], [0,r,c], [0,r,d]], + [[0,1,a], [0,1,b], [0,1,0], [0,1,c], [0,1,d]] + ] + ) [for (row=patch) + translate_points(v=trans, + rotate_points3d(v=orient, + scale_points(v=size, row) + ) + ) + ]; + + +module CR_cube(size=[100,100,100], r=10, splinesteps=8, cheat=false) +{ + s = size-2*[r,r,r]; + h = size/2; + corners = [ + CR_corner([r,r,r], orient=ORIENT_Z, trans=[-size.x/2, -size.y/2, -size.z/2]), + CR_corner([r,r,r], orient=ORIENT_Z_90, trans=[ size.x/2, -size.y/2, -size.z/2]), + CR_corner([r,r,r], orient=ORIENT_Z_180, trans=[ size.x/2, size.y/2, -size.z/2]), + CR_corner([r,r,r], orient=ORIENT_Z_270, trans=[-size.x/2, size.y/2, -size.z/2]), + + CR_corner([r,r,r], orient=ORIENT_ZNEG, trans=[ size.x/2, -size.y/2, size.z/2]), + CR_corner([r,r,r], orient=ORIENT_ZNEG_90, trans=[-size.x/2, -size.y/2, size.z/2]), + CR_corner([r,r,r], orient=ORIENT_ZNEG_180, trans=[-size.x/2, size.y/2, size.z/2]), + CR_corner([r,r,r], orient=ORIENT_ZNEG_270, trans=[ size.x/2, size.y/2, size.z/2]) + ]; + edges = [ + CR_edge([r, r, s.x], orient=ORIENT_X, trans=[ 0, -h.y, -h.z]), + CR_edge([r, r, s.x], orient=ORIENT_X_90, trans=[ 0, h.y, -h.z]), + CR_edge([r, r, s.x], orient=ORIENT_X_180, trans=[ 0, h.y, h.z]), + CR_edge([r, r, s.x], orient=ORIENT_X_270, trans=[ 0, -h.y, h.z]), + + CR_edge([r, r, s.y], orient=ORIENT_Y, trans=[ h.x, 0, -h.z]), + CR_edge([r, r, s.y], orient=ORIENT_Y_90, trans=[-h.x, 0, -h.z]), + CR_edge([r, r, s.y], orient=ORIENT_Y_180, trans=[-h.x, 0, h.z]), + CR_edge([r, r, s.y], orient=ORIENT_Y_270, trans=[ h.x, 0, h.z]), + + CR_edge([r, r, s.z], orient=ORIENT_Z, trans=[-h.x, -h.y, 0]), + CR_edge([r, r, s.z], orient=ORIENT_Z_90, trans=[ h.x, -h.y, 0]), + CR_edge([r, r, s.z], orient=ORIENT_Z_180, trans=[ h.x, h.y, 0]), + CR_edge([r, r, s.z], orient=ORIENT_Z_270, trans=[-h.x, h.y, 0]) + ]; + faces = [ + // Yes, these are degree 1 bezier patches. That means just the four corner points. + // Since these are flat, it doesn't matter what degree they are, and this will reduce calculation overhead. + bezier_patch_flat([s.y, s.z], N=1, orient=ORIENT_X, trans=[ h.x, 0, 0]), + bezier_patch_flat([s.y, s.z], N=1, orient=ORIENT_XNEG, trans=[-h.x, 0, 0]), + + bezier_patch_flat([s.x, s.z], N=1, orient=ORIENT_Y, trans=[ 0, h.y, 0]), + bezier_patch_flat([s.x, s.z], N=1, orient=ORIENT_YNEG, trans=[ 0, -h.y, 0]), + + bezier_patch_flat([s.x, s.y], N=1, orient=ORIENT_Z, trans=[ 0, 0, h.z]), + bezier_patch_flat([s.x, s.y], N=1, orient=ORIENT_ZNEG, trans=[ 0, 0, -h.z]) + ]; + // Generating all the patches above took about 0.05 secs. + + if (cheat) { + // Generating the points for the corners takes 5 seconds on my weak-sauce laptop. + // Hulling it takes less than a second. + hull() bezier_polyhedron(tris=corners, splinesteps=splinesteps); + } else { + // Generating the polyhedron fully from bezier patches takes 12 seconds on my laptop. + bezier_polyhedron(patches=concat(edges, faces), tris=corners, splinesteps=splinesteps); + } +} + + +CR_cube(size=[100,100,100], r=20, splinesteps=16, cheat=false); +cube(1); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap