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