forked from 1b-agent/opencadstudio-upstream
fix(obj): concave OBJ faces are triangulated, not fanned
A fan only covers convex faces; architectural n-gons filled well outside their outline (the sample's faces covered 63% extra area). Fixes #680 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b66b5100bd
commit
4c1ddec646
2 files changed
+35
-6
No files matched your search
+10
-1
@@ -16,6 +16,15 @@ use crate::t;
|
||||
|
||||
/// Triangulate a planar polygon through the kernel.
|
||||
pub(crate) fn triangulate_planar(poly: &[[f64; 3]]) -> Vec<[f64; 3]> {
|
||||
triangulate_planar_indices(poly)
|
||||
.into_iter()
|
||||
.flat_map(|triangle| triangle.map(|index| poly[index]))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Triangles of a planar polygon as indices into `poly` — for callers that
|
||||
/// carry per-vertex data (normals) alongside the positions.
|
||||
pub(crate) fn triangulate_planar_indices(poly: &[[f64; 3]]) -> Vec<[usize; 3]> {
|
||||
if poly.len() < 3 {
|
||||
return Vec::new();
|
||||
}
|
||||
@@ -51,7 +60,7 @@ pub(crate) fn triangulate_planar(poly: &[[f64; 3]]) -> Vec<[f64; 3]> {
|
||||
}
|
||||
triangles
|
||||
.into_iter()
|
||||
.flat_map(|triangle| triangle.map(|index| poly[source[index]]))
|
||||
.map(|triangle| triangle.map(|index| source[index]))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
+25
-5
@@ -51,11 +51,31 @@ pub fn parse_obj(src: &str, color: [f32; 4]) -> Option<MeshModel> {
|
||||
Some((pos_i, norm_i))
|
||||
})
|
||||
.collect();
|
||||
// Fan-triangulate: (0,1,2), (0,2,3), …
|
||||
for k in 1..(descs.len() as isize - 1) {
|
||||
face_verts.push(descs[0]);
|
||||
face_verts.push(descs[k as usize]);
|
||||
face_verts.push(descs[k as usize + 1]);
|
||||
// A fan is only right for convex faces; floor-plan n-gons
|
||||
// are concave, so larger faces go through the kernel's
|
||||
// triangulator and fall back to the fan if it declines. (#680)
|
||||
let corners: Vec<[f64; 3]> = descs
|
||||
.iter()
|
||||
.map(|(pos_i, _)| {
|
||||
let p = positions.get(*pos_i).copied().unwrap_or([0.0; 3]);
|
||||
[p[0] as f64, p[1] as f64, p[2] as f64]
|
||||
})
|
||||
.collect();
|
||||
let triangles = if descs.len() > 3 {
|
||||
crate::entities::mesh::triangulate_planar_indices(&corners)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
if triangles.is_empty() {
|
||||
for k in 1..(descs.len() as isize - 1) {
|
||||
face_verts.push(descs[0]);
|
||||
face_verts.push(descs[k as usize]);
|
||||
face_verts.push(descs[k as usize + 1]);
|
||||
}
|
||||
} else {
|
||||
for triangle in triangles {
|
||||
face_verts.extend(triangle.map(|index| descs[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
Reference in new issue
Block a user