Support adding a child space using the rules of spatial decomposition for each schema

This commit is contained in:
Dion Moult
2024-06-05 14:53:31 +10:00
parent 60ae4e80ce
commit d294be3433
7 changed files with 99 additions and 57 deletions
@@ -36,7 +36,6 @@ class AggregateData:
"relating_object_label": cls.get_relating_object_label(),
"has_related_objects": cls.has_related_objects(),
"total_parts": cls.total_parts(),
"ifc_class": cls.ifc_class(),
"total_linked_aggregate": cls.total_linked_aggregate(),
}
cls.is_loaded = True
@@ -68,12 +67,6 @@ class AggregateData:
def has_related_objects(cls) -> bool:
return bool(cls.get_related_objects())
@classmethod
def ifc_class(cls) -> str:
element = tool.Ifc.get_entity(bpy.context.active_object)
if element:
return element.is_a()
@classmethod
def total_linked_aggregate(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
@@ -275,23 +275,23 @@ class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
bl_options = {"REGISTER", "UNDO"}
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
part_name: bpy.props.StringProperty(name="Name")
obj: bpy.props.StringProperty(options={"HIDDEN"})
element: bpy.props.IntProperty(options={"HIDDEN"})
def invoke(self, context, event):
self.part_name = "My " + self.part_class.lstrip("Ifc")
return context.window_manager.invoke_props_dialog(self)
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) or context.active_object
core.add_part_to_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
tool.Blender,
obj=obj,
obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.element)) if self.element else context.active_object,
part_class=self.part_class,
part_name=self.part_name,
)
tool.Spatial.load_container_manager()
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
@@ -99,19 +99,6 @@ class BIM_PT_aggregate(Panel):
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
op.obj = context.active_object.name
ifc_class = AggregateData.data["ifc_class"]
part_class = ""
if ifc_class == "IfcBuilding":
part_class = "IfcBuildingStorey"
elif ifc_class == "IfcSite":
part_class = "IfcBuilding"
elif ifc_class == "IfcProject":
part_class = "IfcSite"
if part_class != "":
op = layout.operator("bim.add_part_to_object", text="Add " + part_class.lstrip("Ifc"))
op.part_class = part_class
op.obj = context.active_object.name
class BIM_PT_linked_aggregate(Panel):
bl_label = "Linked Aggregates"
@@ -92,19 +92,71 @@ class ProjectTreeData:
def load(cls):
cls.is_loaded = True
cls.data = {
"containers": cls.containers(),
"subelement_class": cls.subelement_class(),
}
@classmethod
def containers(cls):
results = {}
def subelement_class(cls):
results = []
props = bpy.context.scene.BIMProjectTreeProperties
if not (container := props.active_container):
return results
container_class = tool.Ifc.get().by_id(container.ifc_definition_id).is_a()
if tool.Ifc.get_schema() == "IFC2X3":
spatial_elements = tool.Ifc.get().by_type("IfcSpatialStructureElement")
else:
spatial_elements = tool.Ifc.get().by_type("IfcSpatialElement")
for container in spatial_elements:
results[container.id()] = {
"type": container.is_a(),
"id": container.id(),
}
return results
results = {
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
"IfcProject": ["IfcBuilding", "IfcSite", "IfcSpace"],
"IfcSite": ["IfcBuilding", "IfcSite", "IfcSpace"],
"IfcSpace": ["IfcSpace"],
}[container_class]
elif tool.Ifc.get_schema() == "IFC4":
results = {
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
"IfcExternalSpatialElement": ["IfcExternalSpatialElement"],
"IfcProject": ["IfcBuilding", "IfcExternalSpatialElement", "IfcSite", "IfcSpace"],
"IfcSite": ["IfcBuilding", "IfcExternalSpatialElement", "IfcSite", "IfcSpace"],
"IfcSpace": ["IfcSpace"],
}[container_class]
elif tool.Ifc.get_schema() == "IFC4X3":
results = {
"IfcBridge": ["IfcBridge", "IfcBridgePart", "IfcSpace"],
"IfcBridgePart": ["IfcSpace"],
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
"IfcExternalSpatialElement": ["IfcExternalSpatialElement"],
"IfcFacility": ["IfcFacility", "IfcFacilityPartCommon", "IfcSpace"],
"IfcFacilityPartCommon": ["IfcSpace"],
"IfcMarineFacility": ["IfcMarineFacility", "IfcMarinePart", "IfcSpace"],
"IfcMarinePart": ["IfcSpace"],
"IfcProject": [
"IfcBridge",
"IfcBuilding",
"IfcExternalSpatialElement",
"IfcFacility",
"IfcMarineFacility",
"IfcRailway",
"IfcRoad",
"IfcSite",
"IfcSpace",
],
"IfcRailway": ["IfcRailway", "IfcRailwayPart", "IfcSpace"],
"IfcRailwayPart": ["IfcSpace"],
"IfcRoad": ["IfcRoad", "IfcRoadPart", "IfcSpace"],
"IfcRoadPart": ["IfcSpace"],
"IfcSite": [
"IfcBridge",
"IfcBuilding",
"IfcExternalSpatialElement",
"IfcFacility",
"IfcMarineFacility",
"IfcRailway",
"IfcRoad",
"IfcSite",
"IfcSpace",
],
"IfcSpace": ["IfcSpace"],
}[container_class]
return [(r, r, "") for r in results]
@@ -18,7 +18,7 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.spatial.data import SpatialData
from blenderbim.bim.module.spatial.data import ProjectTreeData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -31,10 +31,15 @@ from bpy.props import (
CollectionProperty,
)
import blenderbim.tool as tool
import blenderbim.core.geometry
import ifcopenshell
def get_subelement_class(self, context):
if not ProjectTreeData.is_loaded:
ProjectTreeData.load()
return ProjectTreeData.data["subelement_class"]
def update_elevation(self, context):
if ifc_definition_id := self.ifc_definition_id:
entity = tool.Ifc.get().by_id(ifc_definition_id)
@@ -50,6 +55,7 @@ def update_name(self, context):
def update_active_container_index(self, context):
ProjectTreeData.data["subelement_class"] = ProjectTreeData.subelement_class()
tool.Spatial.load_contained_elements()
@@ -124,6 +130,7 @@ class BIMProjectTreeProperties(PropertyGroup):
elements: CollectionProperty(name="Elements", type=Element)
active_element_index: IntProperty(name="Active Element Index")
total_elements: IntProperty(name="Total Elements")
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
@property
def active_container(self):
@@ -112,15 +112,23 @@ class BIM_PT_project_tree(Panel):
if self.props.active_container:
row = self.layout.row(align=True)
row.label(
text=f"Active {self.props.active_container.ifc_class}: {self.props.active_container.name}",
text=f"Active: {self.props.active_container.name}",
icon="OUTLINER_COLLECTION",
)
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text="")
row.operator("bim.delete_container", icon="X", text="").container = (
self.props.active_container.ifc_definition_id
)
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="")
if self.props.active_container.ifc_class != "IfcProject":
row = self.layout.row(align=True)
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text=f"Isolate {self.props.active_container.ifc_class}")
row.operator("bim.delete_container", icon="X", text="").container = (
self.props.active_container.ifc_definition_id
)
row = self.layout.row(align=True)
row.prop(self.props, "subelement_class", text="")
op = row.operator("bim.add_part_to_object", icon="ADD", text="")
op.element = self.props.active_container.ifc_definition_id
op.part_class = self.props.subelement_class
else:
row = self.layout.row(align=True)
row.label(text="Warning: No Active Container", icon="ERROR")
@@ -139,10 +147,6 @@ class BIM_PT_project_tree(Panel):
if not self.props.active_container:
return
# spatial_data = ProjectTreeData.data["containers"].get(ifc_definition_id, None)
# if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]:
# row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey"
if not self.props.total_elements:
row = self.layout.row()
row.label(text="No Contained Elements", icon="FILE_3D")
@@ -168,7 +172,8 @@ class BIM_UL_containers_manager(UIList):
row = layout.row(align=True)
self.draw_hierarchy(row, item)
row.prop(item, "name", emboss=False, text="")
row.prop(item, "long_name", emboss=False, text="")
if item.long_name:
row.prop(item, "long_name", emboss=False, text="")
col = row.column()
col.alignment = "RIGHT"
col.prop(item, "elevation", emboss=False, text="")
@@ -187,7 +192,9 @@ class BIM_UL_containers_manager(UIList):
)
else:
row.label(text="", icon="BLANK1")
if item.ifc_class == "IfcSite":
if item.ifc_class == "IfcProject":
row.label(text="", icon="FILE")
elif item.ifc_class == "IfcSite":
row.label(text="", icon="WORLD")
elif item.ifc_class == "IfcBuilding":
row.label(text="", icon="HOME")
@@ -195,6 +202,8 @@ class BIM_UL_containers_manager(UIList):
row.label(text="", icon="LINENUMBERS_OFF")
elif item.ifc_class == "IfcSpace":
row.label(text="", icon="ANTIALIASED")
elif "Part" in item.ifc_class:
row.label(text="", icon="MOD_FLUID")
else:
row.label(text="", icon="META_PLANE")
+3 -9
View File
@@ -246,14 +246,7 @@ class Spatial(blenderbim.core.tool.Spatial):
previous_container_index = props.active_container_index
props.containers.clear()
cls.contracted_containers = json.loads(props.contracted_containers)
props.is_container_update_enabled = False
parent = tool.Ifc.get().by_type("IfcProject")[0]
for subelement in ifcopenshell.util.element.get_parts(parent) or []:
if subelement.is_a("IfcSpatialElement") or subelement.is_a("IfcSpatialStructureElement"):
cls.import_spatial_structure(subelement, 0)
props.is_container_update_enabled = True
# triggers spatial manager props setup
cls.import_spatial_structure(tool.Ifc.get().by_type("IfcProject")[0], 0)
props.active_container_index = min(previous_container_index, len(props.containers) - 1)
@classmethod
@@ -264,7 +257,8 @@ class Spatial(blenderbim.core.tool.Spatial):
new.name = element.Name or "Unnamed"
new.description = element.Description or ""
new.long_name = element.LongName or ""
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element)
if not element.is_a("IfcProject"):
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element)
new.is_expanded = element.id() not in cls.contracted_containers
new.level_index = level_index
children = ifcopenshell.util.element.get_parts(element)