Use relevant tool.Geometry functions for querying openings relations

This commit is contained in:
Gorgious56
2025-01-03 12:22:08 +01:00
parent 25af639dae
commit e933ff08f8
5 changed files with 65 additions and 67 deletions
+55 -53
View File
@@ -357,7 +357,7 @@ class FilledOpeningGenerator:
return shape_builder.get_representation(context, [extrusion]) return shape_builder.get_representation(context, [extrusion])
def has_visible_openings(self, element): def has_visible_openings(self, element):
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]: for opening in [r.RelatedOpeningElement for r in tool.Geometry.get_openings(element)]:
if tool.Ifc.get_object(opening): if tool.Ifc.get_object(opening):
return True return True
return False return False
@@ -496,7 +496,7 @@ class AddPotentialOpening(Operator, AddObjectHelper):
if new_matrix: if new_matrix:
obj.matrix_world = new_matrix obj.matrix_world = new_matrix
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
new = props.openings.add() new = props.openings.add()
new.obj = obj new.obj = obj
@@ -523,7 +523,7 @@ class AddPotentialHalfSpaceSolid(Operator, AddObjectHelper):
obj = object_data_add(context, mesh, operator=self) obj = object_data_add(context, mesh, operator=self)
obj.name = "HalfSpaceSolid" obj.name = "HalfSpaceSolid"
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
new = props.openings.add() new = props.openings.add()
new.obj = obj new.obj = obj
@@ -574,7 +574,7 @@ class AddBoolean(Operator, tool.Ifc.Operator):
) )
tool.Model.mark_manual_booleans(element1, booleans) tool.Model.mark_manual_booleans(element1, booleans)
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
bonsai.core.geometry.switch_representation( bonsai.core.geometry.switch_representation(
tool.Ifc, tool.Ifc,
@@ -628,7 +628,7 @@ class ShowBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
booleans.extend(self.get_booleans(item)) booleans.extend(self.get_booleans(item))
props = bpy.context.scene.BIMModelProperties props = bpy.context.scene.BIMModelProperties
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
existing_booleans = { existing_booleans = {
boolean_obj.data.BIMMeshProperties.ifc_boolean_id: boolean_obj boolean_obj.data.BIMMeshProperties.ifc_boolean_id: boolean_obj
@@ -763,7 +763,7 @@ class HideBooleans(Operator, tool.Ifc.Operator):
objects_to_remove.add(obj) objects_to_remove.add(obj)
tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True) tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True)
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
if set_active_obj and set_selected_objs is not None: if set_active_obj and set_selected_objs is not None:
tool.Blender.set_objects_selection(context, set_active_obj, set_selected_objs) tool.Blender.set_objects_selection(context, set_active_obj, set_selected_objs)
@@ -831,38 +831,40 @@ class ShowOpenings(Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = bpy.context.scene.BIMModelProperties
objs = list(context.selected_objects) objs = list(context.selected_objects)
self.update_with_aggregates(objs) # If several parts of an aggregation are selected, the aggregate openings may be queried more than once
objects_element_map = set()
for obj in objs: while objs:
obj = objs.pop(0)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element or not getattr(element, "HasOpenings", None): if element and getattr(element, "Decomposes", None):
continue # Select aggregate recursively
if tool.Ifc.is_moved(obj): if element.Decomposes and (aggregate := element.Decomposes[0].RelatingObject):
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj) aggregate_obj = tool.Ifc.get_object(aggregate)
openings = tool.Model.load_openings( objs.append(aggregate_obj)
[ objects_element_map.add((obj, element))
r.RelatedOpeningElement
for r in element.HasOpenings for obj, element in objects_element_map:
if not tool.Ifc.get_object(r.RelatedOpeningElement) self.show_object_openings(obj, element)
],
)
for opening in openings:
new = props.openings.add()
new.obj = opening
opening.display_type = "WIRE"
DecorationsHandler.install(bpy.context) DecorationsHandler.install(bpy.context)
bpy.ops.bim.update_openings_focus() bpy.ops.bim.update_openings_focus()
return {"FINISHED"} return {"FINISHED"}
def update_with_aggregates(self, objs): def show_object_openings(self, obj, element):
for obj in objs: openings_elements = [rel.RelatedOpeningElement for rel in tool.Geometry.get_openings(element)]
element = tool.Ifc.get_entity(obj) if not openings_elements:
if element and getattr(element, "Decomposes", None): return
if element.Decomposes and (aggregate := element.Decomposes[0].RelatingObject): if tool.Ifc.is_moved(obj):
aggregate_obj = tool.Ifc.get_object(aggregate) bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
objs.append(aggregate_obj) openings_elements_to_load = [o for o in openings_elements if not tool.Ifc.get_object(o)]
openings_objects = tool.Model.load_openings(openings_elements_to_load)
for opening in openings_objects:
self.on_new_opening_obj(opening)
def on_new_opening_obj(self, opening_obj):
new = bpy.context.scene.BIMModelProperties.openings.add()
new.obj = opening_obj
opening_obj.display_type = "WIRE"
class UpdateOpeningsFocus(Operator, tool.Ifc.Operator): class UpdateOpeningsFocus(Operator, tool.Ifc.Operator):
@@ -908,24 +910,25 @@ def hide_openings(context, objects):
objects_to_remove = set() objects_to_remove = set()
for opening_prop in context.scene.BIMModelProperties.openings: for opening_prop in context.scene.BIMModelProperties.openings:
opening_obj = opening_prop.obj opening_obj = opening_prop.obj
if opening_obj: if not opening_obj:
opening_element = tool.Ifc.get_entity(opening_obj) continue
if opening_element: opening_element = tool.Ifc.get_entity(opening_obj)
if not opening_element.is_a("IfcOpeningElement"): if opening_element:
# This opening has been assigned to another ifc class. Remove it from the openings pool. See #3854 if not opening_element.is_a("IfcOpeningElement"):
opening_prop.obj = None # This opening has been assigned to another ifc class. Remove it from the openings pool. See #3854
continue opening_prop.obj = None
building_element = opening_element.VoidsElements[0].RelatingBuildingElement continue
if building_element: building_element = opening_element.VoidsElements[0].RelatingBuildingElement
building_obj = tool.Ifc.get_object(building_element) if building_element:
if building_obj in objects: building_obj = tool.Ifc.get_object(building_element)
tool.Ifc.unlink(element=opening_element) if building_obj in objects:
objects_to_remove.add(opening_obj) tool.Ifc.unlink(element=opening_element)
if opening_obj in objects: objects_to_remove.add(opening_obj)
objects_to_remove.add(opening_obj) if opening_obj in objects:
objects_to_remove.add(opening_obj)
tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True) tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True)
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
bpy.ops.bim.update_openings_focus() bpy.ops.bim.update_openings_focus()
@@ -962,7 +965,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
building_objs, opening_elements = self.get_buildings_and_openings(context) building_objs, opening_elements = self.get_buildings_and_openings(context)
self.edit_openings(building_objs, opening_elements) self.edit_openings(building_objs, opening_elements)
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
tool.Model.reload_body_representation(building_objs) tool.Model.reload_body_representation(building_objs)
bpy.ops.bim.update_openings_focus() bpy.ops.bim.update_openings_focus()
return {"FINISHED"} return {"FINISHED"}
@@ -979,7 +982,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
continue continue
opening_element = tool.Ifc.get_entity(opening_obj) opening_element = tool.Ifc.get_entity(opening_obj)
if opening_element is None: if opening_element is None:
objects_to_remove.add(opening_element) objects_to_remove.add(opening_obj)
continue continue
opening_elements.add(opening_element) opening_elements.add(opening_element)
building_element = opening_element.VoidsElements[0].RelatingBuildingElement building_element = opening_element.VoidsElements[0].RelatingBuildingElement
@@ -995,9 +998,8 @@ class EditOpenings(Operator, tool.Ifc.Operator):
building_obj = tool.Ifc.get_object(building_element) building_obj = tool.Ifc.get_object(building_element)
if building_obj: if building_obj:
building_objs.add(building_obj) building_objs.add(building_obj)
elif hasattr(element, "HasOpenings"): else:
building_element = element for relation in tool.Geometry.get_openings(element):
for relation in building_element.HasOpenings:
opening_element = relation.RelatedOpeningElement opening_element = relation.RelatedOpeningElement
if tool.Ifc.get_object(opening_element): if tool.Ifc.get_object(opening_element):
opening_elements.add(opening_element) opening_elements.add(opening_element)
@@ -157,7 +157,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
tool.Ifc.unlink(element=element2) tool.Ifc.unlink(element=element2)
bpy.data.objects.remove(obj2) bpy.data.objects.remove(obj2)
tool.Model.clear_scene_openings() tool.Model.purge_scene_openings()
context.view_layer.objects.active = obj1 context.view_layer.objects.active = obj1
return {"FINISHED"} return {"FINISHED"}
+1 -1
View File
@@ -560,7 +560,7 @@ class Model:
def import_curve(cls, curve, obj=None, position=None): pass def import_curve(cls, curve, obj=None, position=None): pass
def import_rectangle(cls, obj, position, profile): pass def import_rectangle(cls, obj, position, profile): pass
def load_openings(cls, openings): pass def load_openings(cls, openings): pass
def clear_scene_openings(cls): pass def purge_scene_openings(cls): pass
def get_usage_type(cls, element): pass def get_usage_type(cls, element): pass
def get_material_layer_parameters(cls, element): pass def get_material_layer_parameters(cls, element): pass
def get_manual_booleans(cls, element): pass def get_manual_booleans(cls, element): pass
+2 -2
View File
@@ -1526,8 +1526,8 @@ class Geometry(bonsai.core.tool.Geometry):
Use `.RelatedOpeningElement` to get the opening element. Use `.RelatedOpeningElement` to get the opening element.
""" """
for element in getattr(element, "HasOpenings", ()): for element_rel in getattr(element, "HasOpenings", ()):
yield element yield element_rel
if aggregate := ifcopenshell.util.element.get_aggregate(element): if aggregate := ifcopenshell.util.element.get_aggregate(element):
yield from cls.get_openings(aggregate) yield from cls.get_openings(aggregate)
+6 -10
View File
@@ -494,16 +494,12 @@ class Model(bonsai.core.tool.Model):
return ifc_importer.added_data.values() return ifc_importer.added_data.values()
@classmethod @classmethod
def clear_scene_openings(cls) -> None: def purge_scene_openings(cls) -> None:
"""Clear removed scene openings.""" """Purge removed scene openings."""
props = bpy.context.scene.BIMModelProperties openings = bpy.context.scene.BIMModelProperties.openings
has_deleted_opening = True for i in range(len(openings) - 1, -1, -1):
while has_deleted_opening: if not openings[i].obj:
has_deleted_opening = False openings.remove(i)
for i, opening in enumerate(list(props.openings)):
if not opening.obj:
props.openings.remove(i)
has_deleted_opening = True
@classmethod @classmethod
def get_material_layer_parameters(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]: def get_material_layer_parameters(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]: