diff --git a/math.scad b/math.scad index 702a75e..93524bd 100644 --- a/math.scad +++ b/math.scad @@ -71,6 +71,30 @@ function cdr(list) = len(list)>1?[for (i=[1:len(list)-1]) list[i]]:[]; function reverse(list) = [ for (i = [len(list)-1 : -1 : 0]) list[i] ]; +// Returns a slice of the given array, wrapping around past the beginning, if end < start +function wrap_range(list, start, end) = + (end=len(v))? 0 : ((v[n]*v[n]) + sum_of_squares(v,n+1)); diff --git a/paths.scad b/paths.scad index 57c5b6e..7c08824 100644 --- a/paths.scad +++ b/paths.scad @@ -33,6 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include include +include // Creates a 2D polygon circle, modulated by one or more superimposed @@ -154,7 +155,8 @@ module extrude_2dpath_along_spiral(polyline, h, r, twist=360) { [[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]] ); - polyhedron(points=poly_points, faces=poly_faces, convexity=10); + tri_faces = triangulate_faces(poly_points, poly_faces); + polyhedron(points=poly_points, faces=tri_faces, convexity=10); } @@ -215,7 +217,8 @@ module extrude_2dpath_along_3dpath(polyline, path, convexity=10) { [[for (b = [pline_count-1:-1:0]) b+(path_count-1)*pline_count]] ); - polyhedron(points=poly_points, faces=poly_faces, convexity=convexity); + tri_faces = triangulate_faces(poly_points, poly_faces); + polyhedron(points=poly_points, faces=tri_faces, convexity=convexity); } diff --git a/transforms.scad b/transforms.scad index 3cb6304..f1eb81f 100644 --- a/transforms.scad +++ b/transforms.scad @@ -448,7 +448,7 @@ module xspread(spacing=1,n=2) for (i=[0:n-1]) right((i-(n-1)/2.0)*spacing) child // yspread(25,3) sphere(1) // yspread(25, n=3) sphere(1) // yspread(spacing=20, n=4) sphere(1) -module yspread(spacing=1,n=2) for (i=[0:n-1]) back((i-(n-1)/2.0)*spacing) children(); +module yspread(spacing=1,n=2) for (i=[0:n-1]) back((i-(n-1)/2)*spacing) children(); // Spreads out n copies of the given children along the Z axis. diff --git a/triangulation.scad b/triangulation.scad new file mode 100644 index 0000000..05765c2 --- /dev/null +++ b/triangulation.scad @@ -0,0 +1,134 @@ +use + + +function winding_dir(points, face) = + let(count=len(face)) + sum( + [ + for(i=[0:count-1]) cross( + points[face[(i+1)%count]]-points[face[0]], + points[face[(i+2)%count]]-points[face[(i+1)%count]] + ) + ], + 0 + ) +; + + +function find_convex_vertex(dir, points, face, i=0) = + let(count=len(face), + p0=points[face[i]], + p1=points[face[(i+1)%count]], + p2=points[face[(i+2)%count]] + ) + (len(face)>i)? + (cross(p1-p0, p2-p1)*dir>0)? (i+1)%count : find_convex_vertex(dir, points, face, i+1) + : //This should never happen since there is at least 1 convex vertex. + undef +; + + +function point_in_ear(points, face, tests, i=0) = + (iprev[0])? [test, i] : prev + : + [check_point_in_ear(points[face[i]], tests), i] +; + + +function check_point_in_ear(point, tests) = + let( + result=[ + (point*tests[0][0])-tests[0][1], + (point*tests[1][0])-tests[1][1], + (point*tests[2][0])-tests[2][1] + ] + ) + (result[0]>0 && result[1]>0 && result[2]>0)? result[0] : -1 +; + + +function normalize_vertex_perimeter(v) = + (len(v) < 2)? v : + (v[len(v)-1] != v[0])? v : + [for (i=[0:len(v)-2]) v[i]] +; + + +function triangulate_faces(points, faces) = + [ + for (i=[0 : len(faces)-1]) + let(facet = normalize_vertex_perimeter(faces[i])) + for (face = triangulate_face(points, facet)) + if (face[0]!=face[1] && face[1]!=face[2] && face[2]!=face[0]) face + ] +; + + +function is_last_off_a_line(points, facelist, vertex) = + let( + face=wrap_range(facelist, vertex+1, vertex-1), + count=len(face), + dir=winding_dir(points, face) + ) + 0==sum( + [ + for(i=[0:count-1]) norm( + cross( + points[face[(i+1)%count]]-points[face[0]], + points[face[(i+2)%count]]-points[face[(i+1)%count]] + ) + ) + ] + ) +; + + +function triangulate_face(points, face) = + let(count=len(face)) + (3==count)? + [face] + : + let( + dir=winding_dir(points, face), + cv=find_convex_vertex(dir, points, face), + pv=(count+cv-1)%count, + nv=(cv+1)%count, + p0=points[face[pv]], + p1=points[face[cv]], + p2=points[face[nv]], + tests=[ + [cross(dir, p0-p2), cross(dir, p0-p2)*p0], + [cross(dir, p1-p0), cross(dir, p1-p0)*p1], + [cross(dir, p2-p1), cross(dir, p2-p1)*p2] + ], + ear_test=point_in_ear(points, face, tests), + clipable_ear=(ear_test[0]<0), + diagonal_point=ear_test[1] + ) + (clipable_ear)? // There is no point inside the ear. + is_last_off_a_line(points, face, cv)? + // In the point&line degeneracy clip to somewhere in the middle of the line. + flatten([ + triangulate_face(points, wrap_range(face, cv, (cv+2)%count)), + triangulate_face(points, wrap_range(face, (cv+2)%count, cv)) + ]) + : + // Otherwise the ear is safe to clip. + flatten([ + [wrap_range(face, pv, nv)], + triangulate_face(points, wrap_range(face, nv, pv)) + ]) + : // If there is a point inside the ear, make a diagonal and clip along that. + flatten([ + triangulate_face(points, wrap_range(face, cv, diagonal_point)), + triangulate_face(points, wrap_range(face, diagonal_point, cv)) + ]) +; + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap