mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Added triangulation support code.
This commit is contained in:
parent
f9a441dde8
commit
844d2101e5
4 changed files with 164 additions and 3 deletions
24
math.scad
24
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<start)?
|
||||
concat(
|
||||
[for (i=[start:len(list)-1]) list[i]],
|
||||
[for (i=[0:end]) list[i]]
|
||||
)
|
||||
:
|
||||
[for (i=[start:end]) list[i]]
|
||||
;
|
||||
|
||||
|
||||
// Takes an array of arrays and flattens it by one level.
|
||||
// flatten([[1,2,3], [4,5,[6,7,8]]]) returns [1,2,3,4,5,[6,7,8]]
|
||||
function flatten(l) = [ for (a = l) for (b = a) b ];
|
||||
|
||||
|
||||
// Returns the sum of all entries in the given array.
|
||||
// If passed an array of vectors, returns a vector of sums of each part.
|
||||
// sum([1,2,3]) returns 6.
|
||||
// sum([[1,2,3], [3,4,5], [5,6,7]]) returns [9, 12, 15]
|
||||
function sum(v, i=0) = i<len(v)-1 ? v[i] + sum(v, i+1) : v[i];
|
||||
|
||||
|
||||
// Returns the sum of the square of each element of a vector.
|
||||
function sum_of_squares(v,n=0) = (n>=len(v))? 0 : ((v[n]*v[n]) + sum_of_squares(v,n+1));
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
include <math.scad>
|
||||
include <quaternions.scad>
|
||||
include <triangulation.scad>
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
134
triangulation.scad
Normal file
134
triangulation.scad
Normal file
|
@ -0,0 +1,134 @@
|
|||
use <math.scad>
|
||||
|
||||
|
||||
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) =
|
||||
(i<len(face)-1)?
|
||||
let(
|
||||
prev=point_in_ear(points, face, tests, i+1),
|
||||
test=check_point_in_ear(points[face[i]], tests)
|
||||
)
|
||||
(test>prev[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
|
Loading…
Reference in a new issue