mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-28 15:59:45 +00:00
195 lines
6.4 KiB
OpenSCAD
195 lines
6.4 KiB
OpenSCAD
//////////////////////////////////////////////////////////////////////
|
|
// LibFile: triangulation.scad
|
|
// Functions to triangulate polyhedron faces.
|
|
// To use, add the following lines to the beginning of your file:
|
|
// ```
|
|
// include <BOSL2/std.scad>
|
|
// include <BOSL2/triangulation.scad>
|
|
// ```
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Section: Functions
|
|
|
|
|
|
// Function: face_normal()
|
|
// Description:
|
|
// Given an array of vertices (`points`), and a list of indexes into the
|
|
// vertex array (`face`), returns the normal vector of the face.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// face = The face, given as a list of indices into the vertex array `points`.
|
|
function face_normal(points, face) =
|
|
let(count=len(face))
|
|
unit(
|
|
sum(
|
|
[
|
|
for(i=[0:1:count-1]) cross(
|
|
points[face[(i+1)%count]]-points[face[0]],
|
|
points[face[(i+2)%count]]-points[face[(i+1)%count]]
|
|
)
|
|
]
|
|
)
|
|
)
|
|
;
|
|
|
|
|
|
// Function: find_convex_vertex()
|
|
// Description:
|
|
// Returns the index of a convex point on the given face.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// face = The face, given as a list of indices into the vertex array `points`.
|
|
// facenorm = The normal vector of the face.
|
|
function find_convex_vertex(points, face, facenorm, 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)*facenorm>0)? (i+1)%count :
|
|
find_convex_vertex(points, face, facenorm, i+1)
|
|
) : //This should never happen since there is at least 1 convex vertex.
|
|
undef
|
|
;
|
|
|
|
|
|
// Function: point_in_ear()
|
|
// Description: Determine if a point is in a clipable convex ear.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// face = The face, given as a list of indices into the vertex array `points`.
|
|
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]
|
|
;
|
|
|
|
|
|
// Internal non-exposed function.
|
|
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()
|
|
// Description: Removes the last item in an array if it is the same as the first item.
|
|
// Arguments:
|
|
// v = The array to normalize.
|
|
function normalize_vertex_perimeter(v) =
|
|
let(lv = len(v))
|
|
(lv < 2)? v :
|
|
(v[lv-1] != v[0])? v :
|
|
[for (i=[0:1:lv-2]) v[i]]
|
|
;
|
|
|
|
|
|
// Function: is_only_noncolinear_vertex()
|
|
// Description:
|
|
// Given a face in a polyhedron, and a vertex in that face, returns true
|
|
// if that vertex is the only non-colinear vertex in the face.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// facelist = The face, given as a list of indices into the vertex array `points`.
|
|
// vertex = The index into `facelist`, of the vertex to test.
|
|
function is_only_noncolinear_vertex(points, facelist, vertex) =
|
|
let(
|
|
face=select(facelist, vertex+1, vertex-1),
|
|
count=len(face)
|
|
)
|
|
0==sum(
|
|
[
|
|
for(i=[0:1: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()
|
|
// Description:
|
|
// Given a face in a polyhedron, subdivides the face into triangular faces.
|
|
// Returns an array of faces, where each face is a list of three vertex indices.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// face = The face, given as a list of indices into the vertex array `points`.
|
|
function triangulate_face(points, face) =
|
|
let(
|
|
face = deduplicate_indexed(points,face),
|
|
count = len(face)
|
|
)
|
|
(count < 3)? [] :
|
|
(count == 3)? [face] :
|
|
let(
|
|
facenorm=face_normal(points, face),
|
|
cv=find_convex_vertex(points, face, facenorm)
|
|
)
|
|
assert(!is_undef(cv), "Cannot triangulate self-crossing face perimeters.")
|
|
let(
|
|
pv=(count+cv-1)%count,
|
|
nv=(cv+1)%count,
|
|
p0=points[face[pv]],
|
|
p1=points[face[cv]],
|
|
p2=points[face[nv]],
|
|
tests=[
|
|
[cross(facenorm, p0-p2), cross(facenorm, p0-p2)*p0],
|
|
[cross(facenorm, p1-p0), cross(facenorm, p1-p0)*p1],
|
|
[cross(facenorm, p2-p1), cross(facenorm, 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_only_noncolinear_vertex(points, face, cv)?
|
|
// In the point&line degeneracy clip to somewhere in the middle of the line.
|
|
flatten([
|
|
triangulate_face(points, select(face, cv, (cv+2)%count)),
|
|
triangulate_face(points, select(face, (cv+2)%count, cv))
|
|
])
|
|
:
|
|
// Otherwise the ear is safe to clip.
|
|
flatten([
|
|
[select(face, pv, nv)],
|
|
triangulate_face(points, select(face, nv, pv))
|
|
])
|
|
: // If there is a point inside the ear, make a diagonal and clip along that.
|
|
flatten([
|
|
triangulate_face(points, select(face, cv, diagonal_point)),
|
|
triangulate_face(points, select(face, diagonal_point, cv))
|
|
]);
|
|
|
|
|
|
// Function: triangulate_faces()
|
|
// Description:
|
|
// Subdivides all faces for the given polyhedron that have more than three vertices.
|
|
// Returns an array of faces where each face is a list of three vertex array indices.
|
|
// Arguments:
|
|
// points = Array of vertices for the polyhedron.
|
|
// faces = Array of faces for the polyhedron. Each face is a list of 3 or more indices into the `points` array.
|
|
function triangulate_faces(points, faces) =
|
|
[
|
|
for (face=faces) each
|
|
len(face)==3? [face] :
|
|
triangulate_face(points, normalize_vertex_perimeter(face))
|
|
];
|
|
|
|
|
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|