Prototype Building Storey Manager #3246

This commit is contained in:
Sigma Dimensions
2023-06-03 21:07:20 +01:00
parent c6ef402b60
commit 1003e8de91
12 changed files with 344 additions and 7 deletions
@@ -115,6 +115,8 @@ classes = (
ui.BIM_PT_roof, ui.BIM_PT_roof,
ui.LaunchTypeManager, ui.LaunchTypeManager,
ui.BIM_MT_model, ui.BIM_MT_model,
ui.BIM_PT_GridsSpatialManager,
ui.BIM_PT_Grids,
grid.BIM_OT_add_object, grid.BIM_OT_add_object,
stair.BIM_OT_add_object, stair.BIM_OT_add_object,
stair.BIM_OT_add_clever_stair, stair.BIM_OT_add_clever_stair,
@@ -96,7 +96,7 @@ def add_object(self, context):
obj.BIMObjectProperties.ifc_definition_id = result.id() obj.BIMObjectProperties.ifc_definition_id = result.id()
class BIM_OT_add_object(Operator): class BIM_OT_add_object(Operator, tool.Ifc.Operator):
bl_idname = "mesh.add_grid" bl_idname = "mesh.add_grid"
bl_label = "IFC Grid" bl_label = "IFC Grid"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@@ -108,10 +108,7 @@ class BIM_OT_add_object(Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return IfcStore.get_file() return tool.Ifc.get()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
add_object(self, context) add_object(self, context)
@@ -137,6 +137,30 @@ class BIM_PT_authoring(Panel):
row.operator("bim.generate_spaces_from_walls") row.operator("bim.generate_spaces_from_walls")
class BIM_PT_GridsSpatialManager(Panel):
bl_label = "Grids and Containers"
bl_idname = "BIM_PT_GridsSpatialManager"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_options = {"DEFAULT_CLOSED"}
bl_category = "BlenderBIM"
def draw(self, context):
pass
class BIM_PT_Grids(Panel):
bl_label = "Grid Creator"
bl_idname = "BIM_PT_Grids"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_options = {"DEFAULT_CLOSED"}
bl_parent_id = "BIM_PT_GridsSpatialManager"
def draw(self, context):
self.animation_props = context.scene.BIMAnimationProperties
row = self.layout.row()
row.operator("mesh.add_grid", icon="ADD", text="Add Grids")
class BIM_PT_array(bpy.types.Panel): class BIM_PT_array(bpy.types.Panel):
bl_label = "IFC Array" bl_label = "IFC Array"
bl_idname = "BIM_PT_array" bl_idname = "BIM_PT_array"
@@ -31,11 +31,21 @@ classes = (
operator.SelectContainer, operator.SelectContainer,
operator.SelectSimilarContainer, operator.SelectSimilarContainer,
operator.SelectProduct, operator.SelectProduct,
operator.LoadContainerManager,
operator.EditContainerAttributes,
operator.AddBuildingStorey,
operator.ContractContainer,
operator.ExpandContainer,
operator.DeleteContainer,
prop.SpatialElement, prop.SpatialElement,
prop.BIMSpatialProperties, prop.BIMSpatialProperties,
prop.BIMObjectSpatialProperties, prop.BIMObjectSpatialProperties,
prop.BIMContainer,
prop.BIMSpatialManagerProperties,
ui.BIM_PT_spatial, ui.BIM_PT_spatial,
ui.BIM_UL_containers, ui.BIM_UL_containers,
ui.BIM_UL_containers_manager,
ui.BIM_PT_Storeys,
workspace.Hotkey, workspace.Hotkey,
) )
@@ -45,8 +55,10 @@ def register():
bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=True) bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=True)
bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties) bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties)
bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties) bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
def unregister(): def unregister():
del bpy.types.Scene.BIMSpatialProperties del bpy.types.Scene.BIMSpatialProperties
del bpy.types.Object.BIMObjectSpatialProperties del bpy.types.Object.BIMObjectSpatialProperties
del bpy.types.Scene.BIMSpatialManagerProperties
@@ -36,9 +36,20 @@ class SpatialData:
"is_directly_contained": cls.is_directly_contained(), "is_directly_contained": cls.is_directly_contained(),
"label": cls.label(), "label": cls.label(),
"references": cls.references(), "references": cls.references(),
"containers": cls.containers(),
} }
cls.is_loaded = True cls.is_loaded = True
@classmethod
def containers(cls):
results = {}
for container in tool.Ifc.get().by_type("IfcSpatialElement"):
results[container.id()] = {
"type": container.is_a(),
"id": container.id(),
}
return results
@classmethod @classmethod
def get_parent_container_id(cls): def get_parent_container_id(cls):
container_id = bpy.context.scene.BIMSpatialProperties.active_container_id container_id = bpy.context.scene.BIMSpatialProperties.active_container_id
@@ -21,6 +21,8 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.core.spatial as core import blenderbim.core.spatial as core
import blenderbim.core.geometry
import blenderbim.core.aggregate
import blenderbim.core.root import blenderbim.core.root
import blenderbim.bim.handler import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -171,3 +173,76 @@ class SelectProduct(bpy.types.Operator):
def execute(self, context): def execute(self, context):
core.select_product(tool.Spatial, product=tool.Ifc.get().by_id(self.product)) core.select_product(tool.Spatial, product=tool.Ifc.get().by_id(self.product))
return {"FINISHED"} return {"FINISHED"}
class LoadContainerManager(bpy.types.Operator):
bl_idname = "bim.load_container_manager"
bl_label = "Load Container Manager"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
core.load_container_manager(tool.Spatial)
return {"FINISHED"}
class EditContainerAttributes(bpy.types.Operator):
bl_idname = "bim.edit_container_attributes"
bl_label = "Edit container attributes"
bl_options = {"REGISTER", "UNDO"}
container: bpy.props.IntProperty()
def execute(self, context):
core.edit_container_attributes(tool.Spatial, entity=tool.Ifc.get().by_id(self.container))
return {"FINISHED"}
class ContractContainer(bpy.types.Operator):
bl_idname = "bim.contract_container"
bl_label = "Contract Container"
bl_options = {"REGISTER", "UNDO"}
container: bpy.props.IntProperty()
def execute(self, context):
core.contract_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
return {"FINISHED"}
class ExpandContainer(bpy.types.Operator):
bl_idname = "bim.expand_container"
bl_label = "Expand Container"
bl_options = {"REGISTER", "UNDO"}
container: bpy.props.IntProperty()
def execute(self, context):
core.expand_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
return {"FINISHED"}
class DeleteContainer(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.delete_container"
bl_label = "Delete Container"
bl_options = {"REGISTER", "UNDO"}
container: bpy.props.IntProperty()
def _execute(self, context):
core.delete_container(tool.Ifc, tool.Spatial, tool.Geometry, container=tool.Ifc.get().by_id(self.container))
class AddBuildingStorey(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_building_storey"
bl_label = "Add Storey"
bl_options = {"REGISTER", "UNDO"}
part_class: bpy.props.StringProperty()
def _execute(self, context):
active_container = tool.Spatial.get_active_container()
obj = tool.Ifc.get_object(active_container)
blenderbim.core.aggregate.add_part_to_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
tool.Blender,
obj=obj,
part_class=self.part_class,
part_name="Unnamed",
)
core.load_container_manager(tool.Spatial)
@@ -30,6 +30,30 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
import blenderbim.tool as tool
import blenderbim.core.geometry
def update_elevation(self, context):
entity = tool.Ifc.get().by_id(self.active_container_id)
obj = tool.Ifc.get_object(entity)
if not obj:
return
obj.location.z = self.elevation
def update_active_container_index(self, context):
self.active_container_id = self.containers[self.active_container_index].ifc_definition_id
self.container_name = self.containers[self.active_container_index].name
self.elevation = self.containers[self.active_container_index].elevation
def updateContainerName(self, context):
props = context.scene.BIMSpatialManagerProperties
if not props.is_container_update_enabled or self.name == "Unnamed":
return
tool.Spatial.edit_container_name(tool.Ifc.get().by_id(props.active_container_id), self.name)
props.container_name = self.name
class SpatialElement(PropertyGroup): class SpatialElement(PropertyGroup):
@@ -48,3 +72,23 @@ class BIMSpatialProperties(PropertyGroup):
class BIMObjectSpatialProperties(PropertyGroup): class BIMObjectSpatialProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing") is_editing: BoolProperty(name="Is Editing")
class BIMContainer(PropertyGroup):
name: StringProperty(name="Name", update=updateContainerName)
elevation: FloatProperty(name="Elevation")
level_index: IntProperty(name="Level Index")
has_children: BoolProperty(name="Has Children")
is_expanded: BoolProperty(name="Is Expanded")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMSpatialManagerProperties(PropertyGroup):
containers: CollectionProperty(name="Containers", type=BIMContainer)
contracted_containers: StringProperty(name="Contracted containers", default="[]")
expanded_containers: StringProperty(name="Expanded containers", default="[]")
active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index)
active_container_id: IntProperty(name="Active Container Id")
container_name: StringProperty(name="Container Name")
elevation: FloatProperty(name="Elevation", update=update_elevation)
is_container_update_enabled: BoolProperty(name="Is Container Update Enabled", default=True) # TODO:review
@@ -19,6 +19,7 @@
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.spatial.data import SpatialData from blenderbim.bim.module.spatial.data import SpatialData
import blenderbim.tool as tool
class BIM_PT_spatial(Panel): class BIM_PT_spatial(Panel):
@@ -93,3 +94,70 @@ class BIM_UL_containers(UIList):
text="", text="",
emboss=False, emboss=False,
) )
class BIM_PT_Storeys(Panel):
bl_label = "Spatial Manager"
bl_idname = "BIM_PT_Storeys"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_options = {"DEFAULT_CLOSED"}
bl_parent_id = "BIM_PT_GridsSpatialManager"
@classmethod
def poll(cls, context):
return tool.Ifc.get()
def draw(self, context):
if not SpatialData.is_loaded:
SpatialData.load()
self.props = context.scene.BIMSpatialManagerProperties
row = self.layout.row()
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure")
if self.props.active_container_index < len(self.props.containers):
ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id
row = self.layout.row()
row.alignment = "RIGHT"
if SpatialData.data["containers"][ifc_definition_id]["type"] in ["IfcBuildingStorey", "IfcBuilding"]:
row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey"
row.operator("bim.delete_container", icon="X", text="Delete").container = ifc_definition_id
self.layout.template_list(
"BIM_UL_containers_manager",
"",
self.props,
"containers",
self.props,
"active_container_index",
)
row = self.layout.row()
if self.props.active_container_index < len(self.props.containers):
row.prop(self.props, "container_name", text="")
row.prop(self.props, "elevation", text="")
op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply")
op.container = self.props.containers[self.props.active_container_index].ifc_definition_id
class BIM_UL_containers_manager(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
self.draw_hierarchy(row, item)
split1 = row.split(factor=0.7)
split1.prop(item, "name", emboss=False, text="")
split2 = row.split(factor=1)
split2.label(text=str(item.elevation), icon="BLANK1")
def draw_hierarchy(self, row, item):
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
row.operator(
"bim.contract_container", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).container = item.ifc_definition_id
else:
row.operator(
"bim.expand_container", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).container = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
+19
View File
@@ -96,3 +96,22 @@ def select_similar_container(ifc, spatial, obj=None):
def select_product(spatial, product): def select_product(spatial, product):
spatial.select_products([product]) spatial.select_products([product])
def load_container_manager(spatial):
spatial.load_container_manager()
def edit_container_attributes(spatial, entity=None):
spatial.edit_container_attributes(entity)
spatial.load_container_manager()
def contract_container(spatial, container=None):
spatial.contract_container(container)
spatial.load_container_manager()
def expand_container(spatial, container=None):
spatial.expand_container(container)
spatial.load_container_manager()
def delete_container(ifc, spatial, geometry, container=None):
geometry.delete_ifc_object(ifc.get_object(container))
spatial.load_container_manager()
+8
View File
@@ -747,12 +747,18 @@ class Sequence:
class Spatial: class Spatial:
def can_contain(cls, structure_obj, element_obj): pass def can_contain(cls, structure_obj, element_obj): pass
def can_reference(cls, structure, element): pass def can_reference(cls, structure, element): pass
def contract_container(cls, container): pass
def copy_xy(cls, src_obj, destination_obj): pass def copy_xy(cls, src_obj, destination_obj): pass
def create_new_storey_li(cls, element, level_index): pass
def deselect_objects(cls): pass def deselect_objects(cls): pass
def disable_editing(cls, obj): pass def disable_editing(cls, obj): pass
def duplicate_object_and_data(cls, obj): pass def duplicate_object_and_data(cls, obj): pass
def edit_container_attributes(cls, entity): pass
def edit_container_name(cls, container, name): pass
def enable_editing(cls, obj): pass def enable_editing(cls, obj): pass
def expand_container(cls, container): pass
def filter_products(cls, products, action): pass def filter_products(cls, products, action): pass
def get_active_container(cls): pass
def get_container(cls, element): pass def get_container(cls, element): pass
def get_decomposed_elements(cls, container): pass def get_decomposed_elements(cls, container): pass
def get_object_matrix(cls, obj): pass def get_object_matrix(cls, obj): pass
@@ -760,6 +766,7 @@ class Spatial:
def get_selected_product_types(cls): pass def get_selected_product_types(cls): pass
def get_selected_products(cls): pass def get_selected_products(cls): pass
def import_containers(cls, parent=None): pass def import_containers(cls, parent=None): pass
def load_container_manager(cls): pass
def run_root_copy_class(cls, obj=None): pass def run_root_copy_class(cls, obj=None): pass
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass
def select_object(cls, obj): pass def select_object(cls, obj): pass
@@ -768,6 +775,7 @@ class Spatial:
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
def show_scene_objects(cls): pass def show_scene_objects(cls): pass
@interface @interface
class Structural: class Structural:
def disable_editing_structural_analysis_model(cls): pass def disable_editing_structural_analysis_model(cls): pass
@@ -24,6 +24,7 @@ import numpy as np
import ifcopenshell import ifcopenshell
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.core.style import blenderbim.core.style
import blenderbim.core.spatial
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.import_ifc import blenderbim.bim.import_ifc
from mathutils import Vector from mathutils import Vector
@@ -97,12 +98,15 @@ class Geometry(blenderbim.core.tool.Geometry):
if element.VoidsElements: if element.VoidsElements:
bpy.ops.bim.remove_opening(opening_id=element.id()) bpy.ops.bim.remove_opening(opening_id=element.id())
else: else:
is_spatial = element.is_a("IfcSpatialElement")
if getattr(element, "HasOpenings", None): if getattr(element, "HasOpenings", None):
for rel in element.HasOpenings: for rel in element.HasOpenings:
bpy.ops.bim.remove_opening(opening_id=rel.RelatedOpeningElement.id()) bpy.ops.bim.remove_opening(opening_id=rel.RelatedOpeningElement.id())
for port in ifcopenshell.util.system.get_ports(element): for port in ifcopenshell.util.system.get_ports(element):
blenderbim.core.system.remove_port(tool.Ifc, tool.System, port=port) blenderbim.core.system.remove_port(tool.Ifc, tool.System, port=port)
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element) ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
if is_spatial:
blenderbim.core.spatial.load_container_manager(tool.Spatial)
try: try:
obj.name obj.name
bpy.data.objects.remove(obj) bpy.data.objects.remove(obj)
+75 -2
View File
@@ -22,6 +22,7 @@ import blenderbim.core.tool
import blenderbim.core.root import blenderbim.core.root
import blenderbim.core.spatial import blenderbim.core.spatial
import blenderbim.tool as tool import blenderbim.tool as tool
import json
class Spatial(blenderbim.core.tool.Spatial): class Spatial(blenderbim.core.tool.Spatial):
@@ -110,6 +111,7 @@ class Spatial(blenderbim.core.tool.Spatial):
new.long_name = element.LongName or "" new.long_name = element.LongName or ""
new.has_decomposition = bool(element.IsDecomposedBy) new.has_decomposition = bool(element.IsDecomposedBy)
new.ifc_definition_id = element.id() new.ifc_definition_id = element.id()
new.elevation = element[1]
@classmethod @classmethod
def run_root_copy_class(cls, obj=None): def run_root_copy_class(cls, obj=None):
@@ -151,7 +153,11 @@ class Spatial(blenderbim.core.tool.Spatial):
[obj.select_set(True) for obj in objects] [obj.select_set(True) for obj in objects]
elif action == "isolate": elif action == "isolate":
[obj.hide_set(False) for obj in objects if bpy.context.view_layer.objects.get(obj.name)] [obj.hide_set(False) for obj in objects if bpy.context.view_layer.objects.get(obj.name)]
[obj.hide_set(True) for obj in bpy.context.visible_objects if not obj in objects and bpy.context.view_layer.objects.get(obj.name)] # this is slow [
obj.hide_set(True)
for obj in bpy.context.visible_objects
if not obj in objects and bpy.context.view_layer.objects.get(obj.name)
] # this is slow
elif action == "unhide": elif action == "unhide":
[obj.hide_set(False) for obj in objects if bpy.context.view_layer.objects.get(obj.name)] [obj.hide_set(False) for obj in objects if bpy.context.view_layer.objects.get(obj.name)]
elif action == "hide": elif action == "hide":
@@ -164,7 +170,11 @@ class Spatial(blenderbim.core.tool.Spatial):
@classmethod @classmethod
def show_scene_objects(cls): def show_scene_objects(cls):
[obj.hide_set(False) for obj in bpy.data.scenes["Scene"].objects if bpy.context.view_layer.objects.get(obj.name)] [
obj.hide_set(False)
for obj in bpy.data.scenes["Scene"].objects
if bpy.context.view_layer.objects.get(obj.name)
]
@classmethod @classmethod
def get_selected_products(cls): def get_selected_products(cls):
@@ -185,3 +195,66 @@ class Spatial(blenderbim.core.tool.Spatial):
z = src_obj.location[2] z = src_obj.location[2]
src_obj.location = (destination_obj.location[0], destination_obj.location[1], z) src_obj.location = (destination_obj.location[0], destination_obj.location[1], z)
@classmethod
def load_container_manager(cls):
cls.props = bpy.context.scene.BIMSpatialManagerProperties
cls.props.containers.clear()
cls.contracted_containers = json.loads(cls.props.contracted_containers)
cls.props.is_container_update_enabled = False
parent = tool.Ifc.get().by_type("IfcProject")[0]
for object in ifcopenshell.util.element.get_parts(parent):
if object.is_a("IfcSpatialElement"):
cls.create_new_storey_li(object, 0)
cls.props.is_container_update_enabled = True
@classmethod
def create_new_storey_li(cls, element, level_index):
new = cls.props.containers.add()
new.name = element.Name or "Unnamed"
new.long_name = element.LongName or ""
new.has_decomposition = bool(element.IsDecomposedBy)
new.ifc_definition_id = element.id()
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element)
new.is_expanded = element.id() not in cls.contracted_containers
new.level_index = level_index
if new.has_decomposition:
new.has_children = True
if new.is_expanded:
for related_object in ifcopenshell.util.element.get_parts(element):
if related_object.is_a("IfcSpatialElement"):
cls.create_new_storey_li(related_object, level_index + 1)
@classmethod
def edit_container_attributes(cls, entity):
obj = tool.Ifc.get_object(entity)
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
name = bpy.context.scene.BIMSpatialManagerProperties.container_name
if name != entity.Name:
cls.edit_container_name(entity, name)
@classmethod
def edit_container_name(cls, container, name):
tool.Ifc.run("attribute.edit_attributes", product=container, attributes={"Name": name})
@classmethod
def get_active_container(cls):
props = bpy.context.scene.BIMSpatialManagerProperties
if props.active_container_index < len(props.containers):
container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
return container
@classmethod
def contract_container(cls, container):
props = bpy.context.scene.BIMSpatialManagerProperties
contracted_containers = json.loads(props.contracted_containers)
contracted_containers.append(container.id())
props.contracted_containers = json.dumps(contracted_containers)
@classmethod
def expand_container(cls, container):
props = bpy.context.scene.BIMSpatialManagerProperties
contracted_containers = json.loads(props.contracted_containers)
contracted_containers.remove(container.id())
props.contracted_containers = json.dumps(contracted_containers)