Show voided elements in UI

E.g. show voided wall when window or wall's opening is selected.
Example - https://i.imgur.com/jxjWPeE.png
This commit is contained in:
Andrej730
2025-01-16 16:40:25 +05:00
parent 44131a7fe5
commit a383f60915
2 changed files with 90 additions and 16 deletions
+49 -4
View File
@@ -17,7 +17,9 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import ifcopenshell
import bonsai.tool as tool import bonsai.tool as tool
from typing import Any, Generator, Union
def refresh(): def refresh():
@@ -31,13 +33,25 @@ class VoidsData:
@classmethod @classmethod
def load(cls): def load(cls):
cls.data = {"active_opening": cls.active_opening(), "openings": cls.openings(), "fillings": cls.fillings()} cls.data = {
"active_opening": cls.active_opening(),
"openings": cls.openings(),
"fillings": cls.fillings(),
"voided_element": cls.voided_element(),
"filled_voids": cls.filled_voids(),
}
cls.is_loaded = True cls.is_loaded = True
@classmethod
def get_element_data(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return {"id": element.id(), "Name": element.Name or "Unnamed"}
@classmethod @classmethod
def active_opening(cls): def active_opening(cls):
element = tool.Ifc.get_entity(bpy.context.active_object) element = tool.Ifc.get_entity(bpy.context.active_object)
cls.element = element
if element and element.is_a("IfcOpeningElement"): if element and element.is_a("IfcOpeningElement"):
cls.element = element
return element.id() return element.id()
@classmethod @classmethod
@@ -51,8 +65,14 @@ class VoidsData:
opening = rel.RelatedOpeningElement opening = rel.RelatedOpeningElement
for rel2 in getattr(opening, "HasFillings", []) or []: for rel2 in getattr(opening, "HasFillings", []) or []:
filling = rel2.RelatedBuildingElement filling = rel2.RelatedBuildingElement
has_fillings.append({"id": filling.id(), "Name": filling.Name or "Unnamed"}) has_fillings.append(cls.get_element_data(filling))
results.append({"id": opening.id(), "Name": opening.Name or "Unnamed", "HasFillings": has_fillings})
results.append(
cls.get_element_data(opening)
| {
"HasFillings": has_fillings,
}
)
return results return results
@classmethod @classmethod
@@ -63,9 +83,34 @@ class VoidsData:
results = [] results = []
for rel in getattr(element, "HasFillings", []) or []: for rel in getattr(element, "HasFillings", []) or []:
filling = rel.RelatedBuildingElement filling = rel.RelatedBuildingElement
results.append({"id": filling.id(), "Name": filling.Name or "Unnamed"}) results.append(cls.get_element_data(filling))
return results return results
@classmethod
def get_voided_element_data(cls, opening: ifcopenshell.entity_instance) -> Union[dict[str, Any], None]:
voids_elements = None
if voids := opening.VoidsElements:
voided_element = voids[0].RelatingBuildingElement
voids_elements = cls.get_element_data(voided_element)
return voids_elements
@classmethod
def voided_element(cls) -> Union[dict[str, Any], None]:
if not (element := cls.element) or not element.is_a("IfcOpeningElement"):
return None
return cls.get_voided_element_data(element)
@classmethod
def filled_voids(cls) -> Union[dict[str, Any], None]:
if not (element := cls.element) or not (fills_voids := getattr(element, "FillsVoids", [])):
return None
opening = fills_voids[0].RelatingOpeningElement
result = cls.get_element_data(opening) | {
"VoidsElements": cls.get_voided_element_data(opening),
}
return result
class BooleansData: class BooleansData:
data = {} data = {}
+41 -12
View File
@@ -16,10 +16,20 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import bonsai.tool as tool import bonsai.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from bonsai.bim.module.void.data import BooleansData, VoidsData from bonsai.bim.module.void.data import BooleansData, VoidsData
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
import bpy._typing.rna_enums as rna_enums
OPENING_ICON = "SELECT_SUBTRACT"
FILLING_ICON = "SELECT_INTERSECT"
VOIDED_ELEMENT_ICON = "SELECT_EXTEND"
class BIM_PT_voids(Panel): class BIM_PT_voids(Panel):
@@ -55,34 +65,53 @@ class BIM_PT_voids(Panel):
if not VoidsData.data["fillings"]: if not VoidsData.data["fillings"]:
row = self.layout.row() row = self.layout.row()
row.label(text="No Fillings", icon="SELECT_INTERSECT") row.label(text="No Fillings", icon=FILLING_ICON)
for filling in VoidsData.data["fillings"]: for filling in VoidsData.data["fillings"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=filling["Name"], icon="SELECT_INTERSECT") self.draw_selectable_element_ui(row, filling, "filling", FILLING_ICON)
op = row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
op.ifc_id = filling["id"]
op.tooltip = "Select filling object."
row.operator("bim.remove_filling", icon="X", text="").filling = filling["id"] row.operator("bim.remove_filling", icon="X", text="").filling = filling["id"]
if voided := VoidsData.data["voided_element"]:
row = self.layout.row(align=True)
self.draw_selectable_element_ui(row, voided, "voided", VOIDED_ELEMENT_ICON)
else: else:
if not VoidsData.data["openings"]: if not VoidsData.data["openings"]:
row = self.layout.row() row = self.layout.row()
row.label(text="No Openings", icon="SELECT_SUBTRACT") row.label(text="No Openings", icon=OPENING_ICON)
for opening in VoidsData.data["openings"]: for opening in VoidsData.data["openings"]:
if opening["HasFillings"]: if opening["HasFillings"]:
for filling in opening["HasFillings"]: for filling in opening["HasFillings"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=opening["Name"], icon="SELECT_SUBTRACT") row.label(text=opening["Name"], icon=OPENING_ICON)
row.label(text=filling["Name"], icon="SELECT_INTERSECT") self.draw_selectable_element_ui(row, filling, "filling", FILLING_ICON)
op = row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
op.ifc_id = filling["id"]
op.tooltip = "Select filling object."
else: else:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=opening["Name"], icon="SELECT_SUBTRACT") row.label(text=opening["Name"], icon=OPENING_ICON)
row.operator("bim.remove_opening", icon="X", text="").opening_id = opening["id"] row.operator("bim.remove_opening", icon="X", text="").opening_id = opening["id"]
if (opening := VoidsData.data["filled_voids"]) is None:
row = self.layout.row()
row.label(text="No Voids Filled", icon=VOIDED_ELEMENT_ICON)
else:
row = self.layout.row()
row.label(text="Voided Element:", icon=VOIDED_ELEMENT_ICON)
row = self.layout.row(align=True)
row.label(text=opening["Name"], icon=OPENING_ICON)
voided_element = opening["VoidsElements"]
if voided_element is not None:
self.draw_selectable_element_ui(row, voided_element, "voided", VOIDED_ELEMENT_ICON)
def draw_selectable_element_ui(
self, layout: bpy.types.UILayout, element_data: dict[str, Any], object_hint: str, icon: rna_enums.IconItems
) -> None:
layout.label(text=element_data["Name"], icon=icon)
op = layout.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
op.ifc_id = element_data["id"]
op.tooltip = f"Select {object_hint} object."
class BIM_PT_booleans(Panel): class BIM_PT_booleans(Panel):
bl_label = "Booleans" bl_label = "Booleans"