mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-01 12:40:07 +00:00
Support adding a child space using the rules of spatial decomposition for each schema
This commit is contained in:
@@ -36,7 +36,6 @@ class AggregateData:
|
|||||||
"relating_object_label": cls.get_relating_object_label(),
|
"relating_object_label": cls.get_relating_object_label(),
|
||||||
"has_related_objects": cls.has_related_objects(),
|
"has_related_objects": cls.has_related_objects(),
|
||||||
"total_parts": cls.total_parts(),
|
"total_parts": cls.total_parts(),
|
||||||
"ifc_class": cls.ifc_class(),
|
|
||||||
"total_linked_aggregate": cls.total_linked_aggregate(),
|
"total_linked_aggregate": cls.total_linked_aggregate(),
|
||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
@@ -68,12 +67,6 @@ class AggregateData:
|
|||||||
def has_related_objects(cls) -> bool:
|
def has_related_objects(cls) -> bool:
|
||||||
return bool(cls.get_related_objects())
|
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
|
@classmethod
|
||||||
def total_linked_aggregate(cls):
|
def total_linked_aggregate(cls):
|
||||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
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"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
|
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
|
||||||
part_name: bpy.props.StringProperty(name="Name")
|
part_name: bpy.props.StringProperty(name="Name")
|
||||||
obj: bpy.props.StringProperty(options={"HIDDEN"})
|
element: bpy.props.IntProperty(options={"HIDDEN"})
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
self.part_name = "My " + self.part_class.lstrip("Ifc")
|
self.part_name = "My " + self.part_class.lstrip("Ifc")
|
||||||
return context.window_manager.invoke_props_dialog(self)
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) or context.active_object
|
|
||||||
core.add_part_to_object(
|
core.add_part_to_object(
|
||||||
tool.Ifc,
|
tool.Ifc,
|
||||||
tool.Aggregate,
|
tool.Aggregate,
|
||||||
tool.Collector,
|
tool.Collector,
|
||||||
tool.Blender,
|
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_class=self.part_class,
|
||||||
part_name=self.part_name,
|
part_name=self.part_name,
|
||||||
)
|
)
|
||||||
|
tool.Spatial.load_container_manager()
|
||||||
|
|
||||||
|
|
||||||
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
|
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 = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
op.obj = context.active_object.name
|
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):
|
class BIM_PT_linked_aggregate(Panel):
|
||||||
bl_label = "Linked Aggregates"
|
bl_label = "Linked Aggregates"
|
||||||
|
|||||||
@@ -92,19 +92,71 @@ class ProjectTreeData:
|
|||||||
def load(cls):
|
def load(cls):
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
cls.data = {
|
cls.data = {
|
||||||
"containers": cls.containers(),
|
"subelement_class": cls.subelement_class(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def containers(cls):
|
def subelement_class(cls):
|
||||||
results = {}
|
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":
|
if tool.Ifc.get_schema() == "IFC2X3":
|
||||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
results = {
|
||||||
else:
|
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
|
||||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialElement")
|
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
|
||||||
for container in spatial_elements:
|
"IfcProject": ["IfcBuilding", "IfcSite", "IfcSpace"],
|
||||||
results[container.id()] = {
|
"IfcSite": ["IfcBuilding", "IfcSite", "IfcSpace"],
|
||||||
"type": container.is_a(),
|
"IfcSpace": ["IfcSpace"],
|
||||||
"id": container.id(),
|
}[container_class]
|
||||||
}
|
elif tool.Ifc.get_schema() == "IFC4":
|
||||||
return results
|
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
|
import bpy
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
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.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@@ -31,10 +31,15 @@ from bpy.props import (
|
|||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.core.geometry
|
|
||||||
import ifcopenshell
|
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):
|
def update_elevation(self, context):
|
||||||
if ifc_definition_id := self.ifc_definition_id:
|
if ifc_definition_id := self.ifc_definition_id:
|
||||||
entity = tool.Ifc.get().by_id(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):
|
def update_active_container_index(self, context):
|
||||||
|
ProjectTreeData.data["subelement_class"] = ProjectTreeData.subelement_class()
|
||||||
tool.Spatial.load_contained_elements()
|
tool.Spatial.load_contained_elements()
|
||||||
|
|
||||||
|
|
||||||
@@ -124,6 +130,7 @@ class BIMProjectTreeProperties(PropertyGroup):
|
|||||||
elements: CollectionProperty(name="Elements", type=Element)
|
elements: CollectionProperty(name="Elements", type=Element)
|
||||||
active_element_index: IntProperty(name="Active Element Index")
|
active_element_index: IntProperty(name="Active Element Index")
|
||||||
total_elements: IntProperty(name="Total Elements")
|
total_elements: IntProperty(name="Total Elements")
|
||||||
|
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_container(self):
|
def active_container(self):
|
||||||
|
|||||||
@@ -112,15 +112,23 @@ class BIM_PT_project_tree(Panel):
|
|||||||
if self.props.active_container:
|
if self.props.active_container:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(
|
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",
|
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="")
|
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:
|
else:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(text="Warning: No Active Container", icon="ERROR")
|
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:
|
if not self.props.active_container:
|
||||||
return
|
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:
|
if not self.props.total_elements:
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.label(text="No Contained Elements", icon="FILE_3D")
|
row.label(text="No Contained Elements", icon="FILE_3D")
|
||||||
@@ -168,7 +172,8 @@ class BIM_UL_containers_manager(UIList):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
self.draw_hierarchy(row, item)
|
self.draw_hierarchy(row, item)
|
||||||
row.prop(item, "name", emboss=False, text="")
|
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 = row.column()
|
||||||
col.alignment = "RIGHT"
|
col.alignment = "RIGHT"
|
||||||
col.prop(item, "elevation", emboss=False, text="")
|
col.prop(item, "elevation", emboss=False, text="")
|
||||||
@@ -187,7 +192,9 @@ class BIM_UL_containers_manager(UIList):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row.label(text="", icon="BLANK1")
|
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")
|
row.label(text="", icon="WORLD")
|
||||||
elif item.ifc_class == "IfcBuilding":
|
elif item.ifc_class == "IfcBuilding":
|
||||||
row.label(text="", icon="HOME")
|
row.label(text="", icon="HOME")
|
||||||
@@ -195,6 +202,8 @@ class BIM_UL_containers_manager(UIList):
|
|||||||
row.label(text="", icon="LINENUMBERS_OFF")
|
row.label(text="", icon="LINENUMBERS_OFF")
|
||||||
elif item.ifc_class == "IfcSpace":
|
elif item.ifc_class == "IfcSpace":
|
||||||
row.label(text="", icon="ANTIALIASED")
|
row.label(text="", icon="ANTIALIASED")
|
||||||
|
elif "Part" in item.ifc_class:
|
||||||
|
row.label(text="", icon="MOD_FLUID")
|
||||||
else:
|
else:
|
||||||
row.label(text="", icon="META_PLANE")
|
row.label(text="", icon="META_PLANE")
|
||||||
|
|
||||||
|
|||||||
@@ -246,14 +246,7 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
previous_container_index = props.active_container_index
|
previous_container_index = props.active_container_index
|
||||||
props.containers.clear()
|
props.containers.clear()
|
||||||
cls.contracted_containers = json.loads(props.contracted_containers)
|
cls.contracted_containers = json.loads(props.contracted_containers)
|
||||||
props.is_container_update_enabled = False
|
cls.import_spatial_structure(tool.Ifc.get().by_type("IfcProject")[0], 0)
|
||||||
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
|
|
||||||
props.active_container_index = min(previous_container_index, len(props.containers) - 1)
|
props.active_container_index = min(previous_container_index, len(props.containers) - 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -264,7 +257,8 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
new.name = element.Name or "Unnamed"
|
new.name = element.Name or "Unnamed"
|
||||||
new.description = element.Description or ""
|
new.description = element.Description or ""
|
||||||
new.long_name = element.LongName 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.is_expanded = element.id() not in cls.contracted_containers
|
||||||
new.level_index = level_index
|
new.level_index = level_index
|
||||||
children = ifcopenshell.util.element.get_parts(element)
|
children = ifcopenshell.util.element.get_parts(element)
|
||||||
|
|||||||
Reference in New Issue
Block a user