From 089edff1db5dcc821a9576b6d318c06ccdfe737b Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Thu, 30 Oct 2025 20:40:06 -0500 Subject: [PATCH] fix for #7296: handle openings without Blender representation in get_gross_top_area (#7297) --- .../bonsai/bim/module/qto/calculator.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/qto/calculator.py b/src/bonsai/bonsai/bim/module/qto/calculator.py index 7a8affb45f..6391fbeffe 100644 --- a/src/bonsai/bonsai/bim/module/qto/calculator.py +++ b/src/bonsai/bonsai/bim/module/qto/calculator.py @@ -834,7 +834,6 @@ def get_end_area(obj: bpy.types.Object) -> float: return end_area - def get_gross_top_area(obj: bpy.types.Object, angle: float = 45) -> float: """_summary_: Returns the gross top area of the object. @@ -859,8 +858,23 @@ def get_gross_top_area(obj: bpy.types.Object, angle: float = 45) -> float: entity = ifc.by_guid(opening_id) open_obj = tool.Ifc.get_object(entity) - assert isinstance(open_obj, bpy.types.Object) - opening_area += get_net_top_area(open_obj, angle=angle) + + # Check if the opening has a Blender object representation + if open_obj is None: + # If no Blender object exists, create a temporary mesh from the IFC geometry + try: + mesh = get_gross_element_mesh(entity) + temp_obj = bpy.data.objects.new("TempOpening", mesh) + opening_area += get_net_top_area(temp_obj, angle=angle) + delete_obj(temp_obj) + delete_mesh(mesh) + except Exception as e: + # If we can't create geometry, skip this opening + print(f"Warning: Could not calculate opening area for {opening_id}: {e}") + continue + else: + # Opening has a Blender representation, use it directly + opening_area += get_net_top_area(open_obj, angle=angle) else: continue @@ -872,6 +886,7 @@ def get_gross_top_area(obj: bpy.types.Object, angle: float = 45) -> float: return area + opening_area + # curently net top area is larger then projected area, because its taking into account internal polygons, or window sills def get_net_top_area(obj: bpy.types.Object, angle: float = 45, ignore_internal: bool = True) -> float: """_summary_: Returns the net top area of the object.