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/>.
import bpy
import ifcopenshell
import bonsai.tool as tool
from typing import Any, Generator, Union
def refresh():
@@ -31,13 +33,25 @@ class VoidsData:
@classmethod
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
@classmethod
def get_element_data(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return {"id": element.id(), "Name": element.Name or "Unnamed"}
@classmethod
def active_opening(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
cls.element = element
if element and element.is_a("IfcOpeningElement"):
cls.element = element
return element.id()
@classmethod
@@ -51,8 +65,14 @@ class VoidsData:
opening = rel.RelatedOpeningElement
for rel2 in getattr(opening, "HasFillings", []) or []:
filling = rel2.RelatedBuildingElement
has_fillings.append({"id": filling.id(), "Name": filling.Name or "Unnamed"})
results.append({"id": opening.id(), "Name": opening.Name or "Unnamed", "HasFillings": has_fillings})
has_fillings.append(cls.get_element_data(filling))
results.append(
cls.get_element_data(opening)
| {
"HasFillings": has_fillings,
}
)
return results
@classmethod
@@ -63,9 +83,34 @@ class VoidsData:
results = []
for rel in getattr(element, "HasFillings", []) or []:
filling = rel.RelatedBuildingElement
results.append({"id": filling.id(), "Name": filling.Name or "Unnamed"})
results.append(cls.get_element_data(filling))
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:
data = {}
+41 -12
View File
@@ -16,10 +16,20 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.tool as tool
from bpy.types import Panel, UIList
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):
@@ -55,34 +65,53 @@ class BIM_PT_voids(Panel):
if not VoidsData.data["fillings"]:
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"]:
row = self.layout.row(align=True)
row.label(text=filling["Name"], icon="SELECT_INTERSECT")
op = row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
op.ifc_id = filling["id"]
op.tooltip = "Select filling object."
self.draw_selectable_element_ui(row, filling, "filling", FILLING_ICON)
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:
if not VoidsData.data["openings"]:
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"]:
if opening["HasFillings"]:
for filling in opening["HasFillings"]:
row = self.layout.row(align=True)
row.label(text=opening["Name"], icon="SELECT_SUBTRACT")
row.label(text=filling["Name"], icon="SELECT_INTERSECT")
op = row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF")
op.ifc_id = filling["id"]
op.tooltip = "Select filling object."
row.label(text=opening["Name"], icon=OPENING_ICON)
self.draw_selectable_element_ui(row, filling, "filling", FILLING_ICON)
else:
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"]
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):
bl_label = "Booleans"