mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
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:
@@ -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 = {}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user