Fix shapely topology crash in boundary generation

Add buffer(0) validation for space_face_polygon and face_polygon
before intersection, following the same pattern as tool/cad.py.

Wrap the intersection in try/except for shapely.errors.GEOSException
to catch remaining topology errors. On exception, set
bonsai.last_error (so the 'Copy Error Message To Clipboard' button
appears in the UI), report an ERROR to the operator, and continue
processing other face pairs instead of crashing.

Generated with the assistance of an AI coding tool.
This commit is contained in:
CyrilWaechter
2026-07-30 15:28:23 +02:00
parent 2ad4ae8de5
commit 8b73de5d42
@@ -18,6 +18,7 @@
import logging
import multiprocessing
import traceback
from math import acos, degrees, inf, pi, radians
from typing import Optional, Union
@@ -38,6 +39,7 @@ import shapely.ops
from ifcopenshell.util.shape_builder import ShapeBuilder
from mathutils import Matrix, Vector
import bonsai
import bonsai.bim.import_ifc as import_ifc
import bonsai.core.attribute as core
import bonsai.core.geometry
@@ -804,12 +806,26 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
space_face_polygon = shapely.Polygon(
[tuple((space_face_matrix_i @ v).xy) for v in space_face_verts]
)
if not space_face_polygon.is_valid:
space_face_polygon = space_face_polygon.buffer(0)
space_matrix_world_i = space_obj.matrix_world.inverted()
face_verts = [space_matrix_world_i @ building_obj.matrix_world @ v.co.copy() for v in face.verts]
face_polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in face_verts])
if not face_polygon.is_valid:
face_polygon = face_polygon.buffer(0)
gross_boundary_polygon = space_face_polygon.intersection(face_polygon)
try:
gross_boundary_polygon = space_face_polygon.intersection(face_polygon)
except shapely.errors.GEOSException:
bonsai.last_error = traceback.format_exc()
element_name = building_element.Name or building_element.is_a()
self.report(
{"ERROR"},
f"Skipping invalid geometry for {element_name} (shapely topology error). "
"See 'Copy Error Message To Clipboard' for details.",
)
continue
if type(gross_boundary_polygon) == shapely.GeometryCollection:
for geom in gross_boundary_polygon.geoms: