Option to apply openings to the geometry #5666

Button location - https://i.imgur.com/6zSv8vV.png
Now it's possible to "bake" openings to the geometry, The openings representation will be marked as "Reference".
This commit is contained in:
Andrej730
2024-10-31 16:58:45 +05:00
parent b39f96e2ee
commit 06a6c2d79b
2 changed files with 41 additions and 8 deletions
@@ -386,15 +386,28 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Update Representation"
bl_description = (
"Write selected objects representations to IFC.\n"
"A star in the operator name indicates that active object representation in IFC is not yet synced with Blender"
"A star in the operator name indicates that active object representation in IFC is not yet synced with Blender.\n"
"ALT+CLICK to apply openings to the mesh"
)
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
ifc_representation_class: bpy.props.StringProperty()
apply_openings: bpy.props.BoolProperty(
name="Apply Openings",
description=(
"Whether to apply openings to the mesh.\n"
"If False, operator will skip updating representation that has openings"
),
default=False,
options={"SKIP_SAVE"},
)
from_ui = False
def invoke(self, context, event):
if event.type == "LEFTMOUSE" and event.alt:
self.apply_openings = True
self.from_ui = True
return self.execute(context)
@@ -426,7 +439,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
# NOTE: Currently iterator doesn't detect whether opening is actually affected the representation
# or it's just present on the element. In theory, we can also allow editing representations
# if we know that representation wasn't affected by existing openings.
if tool.Geometry.has_openings(product) and obj.data.BIMMeshProperties.has_openings_applied:
has_openings = tool.Geometry.has_openings(product) and obj.data.BIMMeshProperties.has_openings_applied
if has_openings and not self.apply_openings:
# Meshlike things with openings can only be updated without openings applied.
if self.from_ui:
self.report({"ERROR"}, f"Object '{obj.name}' has openings - representation cannot be updated.")
@@ -514,6 +528,17 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
for inverse in self.file.get_inverse(old_representation):
ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation)
# As openings are already 'baked' to the geometry, we mark their representation as 'Reference'
# as they're not part of the object representation anymore.
if has_openings and self.apply_openings:
ifc_context = new_representation.ContextOfItems
for opening_rel in tool.Geometry.get_openings(product):
opening = opening_rel.RelatedOpeningElement
representation = ifcopenshell.util.representation.get_representation(opening, ifc_context)
if not representation:
continue
representation.RepresentationIdentifier = "Reference"
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
+14 -6
View File
@@ -48,7 +48,7 @@ from collections import defaultdict
from math import radians, pi
from mathutils import Vector, Matrix
from bonsai.bim.ifc import IfcStore
from typing import Union, Iterable, Optional, Literal, Iterator, List, TYPE_CHECKING, get_args
from typing import Union, Iterable, Optional, Literal, Iterator, List, TYPE_CHECKING, get_args, Generator
from typing_extensions import TypeIs
if TYPE_CHECKING:
@@ -1470,12 +1470,20 @@ class Geometry(bonsai.core.tool.Geometry):
return use_immediate_repr
@classmethod
def has_openings(cls, element: ifcopenshell.entity_instance) -> bool:
if getattr(element, "HasOpenings", None):
return True
def get_openings(cls, element: ifcopenshell.entity_instance) -> Generator[ifcopenshell.entity_instance, None, None]:
"""Get element openings as IfcRelVoidsElements.
Use `.RelatedOpeningElement` to get the opening element.
"""
for element in getattr(element, "HasOpenings", ()):
yield element
if aggregate := ifcopenshell.util.element.get_aggregate(element):
return cls.has_openings(aggregate)
return False
yield from cls.get_openings(aggregate)
@classmethod
def has_openings(cls, element: ifcopenshell.entity_instance) -> bool:
return bool(next(cls.get_openings(element), False))
@classmethod
def get_elements_by_representation(