From f9dd6e7235b13cbcaaf1909767205739b88536d9 Mon Sep 17 00:00:00 2001 From: Adrian Mariano <avm4@cornell.edu> Date: Sun, 30 Jan 2022 11:24:53 -0500 Subject: [PATCH] Speed improvement "icosa2" for spheroid --- shapes3d.scad | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/shapes3d.scad b/shapes3d.scad index 26c893c..16240cc 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -1617,8 +1617,8 @@ function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient= // - `style="orig"` constructs a sphere the same way that the OpenSCAD `sphere()` built-in does. // - `style="aligned"` constructs a sphere where, if `$fn` is a multiple of 4, it has vertices at all axis maxima and minima. ie: its bounding box is exactly the sphere diameter in length on all three axes. This is the default. // - `style="stagger"` forms a sphere where all faces are triangular, but the top and bottom poles have thinner triangles. -// - `style="octa"` forms a sphere by subdividing an octahedron (8-sided platonic solid). This makes more uniform faces over the entirety of the sphere, and guarantees the bounding box is the sphere diameter in size on all axes. The effective `$fn` value is quantized to a multiple of 4, though. This is used in constructing rounded corners for various other shapes. -// - `style="icosa"` forms a sphere by subdividing an icosahedron (20-sided platonic solid). This makes even more uniform faces over the entirety of the sphere. The effective `$fn` value is quantized to a multiple of 5, though. +// - `style="octa"` forms a sphere by subdividing an octahedron (8-sided platonic solid). This makes more uniform faces over the entirety of the sphere, and guarantees the bounding box is the sphere diameter in size on all axes. The effective `$fn` value is quantized to a multiple of 4. This is used in constructing rounded corners for various other shapes. +// - `style="icosa"` forms a sphere by subdividing an icosahedron (20-sided platonic solid). This makes even more uniform faces over the entirety of the sphere. The effective `$fn` value is quantized to a multiple of 5. // Arguments: // r = Radius of the spheroid. // style = The style of the spheroid's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "aligned" @@ -1680,6 +1680,12 @@ module spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, orie } +// p is a list of 3 points defining a triangle in any dimension. N is the number of extra points +// to add, so output triangle has N+2 points on each side. +function _subsample_triangle(p,N) = + [for(i=[0:N+1]) [for (j=[0:N+1-i]) unit(lerp(p[0],p[1],i/(N+1)) + (p[2]-p[0])*j/(N+1))]]; + + function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, orient=UP) = let( r = get_radius(r=r, d=d, dflt=1), @@ -1688,7 +1694,36 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or octa_steps = round(max(4,hsides)/4), icosa_steps = round(max(5,hsides)/5), rr = circum? (r / cos(90/vsides) / cos(180/hsides)) : r, - stagger = style=="stagger", + stagger = style=="stagger" + ) + style=="icosa2" ? // subdivide faces of an icosahedron and project them onto a sphere + let( + N = icosa_steps-1, + // construct an icosahedron + icovert=[ for(i=[-1,1], j=[-1,1]) each [[0,i,j*PHI], [i,j*PHI,0], [j*PHI,0,i]]], + icoface = hull(icovert), + // Subsample face 0 of the icosahedron + face0 = select(icovert,icoface[0]), + sampled = rr * _subsample_triangle(face0,N), + dir0 = mean(face0), + point0 = face0[0]-dir0, + // Make a rotated copy of the subsampled triangle on each icosahedral face + tri_list = [sampled, + for(i=[1:1:len(icoface)-1]) + let(face = select(icovert,icoface[i])) + apply(frame_map(z=mean(face),x=face[0]-mean(face)) + *frame_map(z=dir0,x=point0,reverse=true), + sampled)], + // faces for the first triangle group + faces = vnf_tri_array(tri_list[0])[1], + size = repeat((N+2)*(N+3)/2,3), + // Expand to full face list + fullfaces = [for(i=idx(tri_list)) each [for(f=faces) f+i*size]], + fullvert = flatten(flatten(tri_list)) // eliminate triangle structure + ) + [reorient(anchor,spin,orient, r=r, p=fullvert), fullfaces] + : + let( verts = style=="orig"? [ for (i=[0:1:vsides-1]) let(phi = (i+0.5)*180/(vsides)) for (j=[0:1:hsides-1]) let(theta = j*360/hsides)