mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
ifcopenshell.util.boundary: make face matrix robust against collinear first vertices
_face_matrix_from_verts used only the first 3 vertices and sb.np_normal, which divides by zero when they are collinear. Triangulated meshes from generated spaces often start with collinear boundary vertices, producing NaN matrices and a shapely LinearRing error. Walk the polygon to find a non-degenerate normal and edge. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -261,7 +261,7 @@ def auto_generate_boundaries(
|
||||
)
|
||||
space_face_normal_world = space_matrix_3x3 @ space_face_normal_local
|
||||
|
||||
face_matrix = _face_matrix_from_verts(space_verts_l[:3])
|
||||
face_matrix = _face_matrix_from_verts(space_verts_l)
|
||||
face_matrix_inv = np.linalg.inv(face_matrix)
|
||||
space_face_polygon = _verts_to_polygon(space_verts_l, face_matrix_inv, snap=1e-6)
|
||||
if not space_face_polygon.is_valid:
|
||||
@@ -870,11 +870,29 @@ def _face_normal(verts: np.ndarray) -> Optional[np.ndarray]:
|
||||
|
||||
|
||||
def _face_matrix_from_verts(verts3: np.ndarray) -> np.ndarray:
|
||||
"""Build a 4x4 face-local coordinate matrix from 3 vertices."""
|
||||
p1, p2, p3 = verts3[0], verts3[1], verts3[2]
|
||||
z = sb.np_normal([p1, p2, p3])
|
||||
x = sb.np_normalized(p2 - p1)
|
||||
return ifcopenshell.util.placement.a2p(o=p1, z=z, x=x)
|
||||
"""Build a 4x4 face-local coordinate matrix from a polygon's vertices.
|
||||
|
||||
The first three vertices may be collinear in triangulated meshes, so the
|
||||
normal is computed from the first non-degenerate triple and the X axis is
|
||||
taken from the first non-degenerate edge.
|
||||
"""
|
||||
p1 = np.asarray(verts3[0])
|
||||
|
||||
normal = _face_normal(verts3)
|
||||
if normal is None:
|
||||
raise ValueError("Cannot build face matrix from a degenerate polygon")
|
||||
|
||||
x = np.zeros(3)
|
||||
for i in range(len(verts3) - 1):
|
||||
edge = np.asarray(verts3[i + 1]) - np.asarray(verts3[i])
|
||||
edge_norm = np.linalg.norm(edge)
|
||||
if edge_norm > 1e-8:
|
||||
x = edge / edge_norm
|
||||
break
|
||||
if np.linalg.norm(x) < 1e-8:
|
||||
raise ValueError("Cannot find a non-degenerate edge for the face matrix")
|
||||
|
||||
return ifcopenshell.util.placement.a2p(o=p1, z=normal, x=x)
|
||||
|
||||
|
||||
def _verts_to_polygon(verts: np.ndarray, face_matrix_inv: np.ndarray, snap: float = 0) -> shapely.Polygon:
|
||||
|
||||
Reference in New Issue
Block a user