Improve UI to change spatial container to prevent accidentally selecting incorrect objects

This commit is contained in:
Dion Moult
2021-02-21 11:24:27 +11:00
parent 0178804feb
commit 0286cd370d
8 changed files with 177 additions and 42 deletions
@@ -41,12 +41,7 @@ class BIM_PT_documents(Panel):
if self.props.is_editing:
self.layout.template_list(
"BIM_UL_documents",
"",
self.props,
"documents",
self.props,
"active_document_index",
"BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index"
)
if self.props.active_document_id:
@@ -182,6 +182,7 @@ class UnassignClass(bpy.types.Operator):
self.remove_representations(obj, product)
IfcStore.unlink_element(product, obj)
remove_product.Usecase(self.file, {"product": product}).execute()
# TODO: remove object placement
if "/" in obj.name and obj.name[0:3] == "Ifc":
obj.name = "/".join(obj.name.split("/")[1:])
if obj.data and obj.data.name == "Void":
@@ -1,18 +1,25 @@
import bpy
from . import ui, operator
from . import ui, prop, operator
classes = (
operator.AssignContainer,
operator.RemoveContainer,
operator.EnableEditingContainer,
operator.DisableEditingContainer,
operator.ChangeSpatialLevel,
prop.SpatialElement,
prop.BIMSpatialProperties,
prop.BIMObjectSpatialProperties,
ui.BIM_PT_spatial,
ui.BIM_UL_spatial_elements,
)
def register():
pass
bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties)
bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
def unregister():
pass
del bpy.types.Scene.BIMSpatialProperties
del bpy.types.Object.BIMObjectSpatialProperties
@@ -3,13 +3,21 @@ from blenderbim.bim.ifc import IfcStore
class Data:
products = {}
spatial_elements = {}
@classmethod
def purge(cls):
cls.products = {}
cls.spatial_elements = {}
@classmethod
def load(cls, product_id):
def load(cls, product_id=None):
if product_id:
return cls.load_product(product_id)
cls.load_spatial_elements()
@classmethod
def load_product(cls, product_id):
file = IfcStore.get_file()
if not file:
return
@@ -21,3 +29,27 @@ class Data:
cls.products[product_id] = {"type": structure.is_a(), "Name": structure.Name, "id": int(structure.id())}
else:
cls.products[product_id] = {"type": None, "Name": None, "id": None}
@classmethod
def load_spatial_elements(cls):
file = IfcStore.get_file()
if not file:
return
cls.spatial_elements = {}
ifc_class = "IfcSpatialElement"
if file.schema == "IFC2X3":
ifc_class = "IfcSpatialStructureElement"
for element in file.by_type(ifc_class):
decomposes = None
if element.Decomposes:
decomposes = element.Decomposes[0].RelatingObject.id()
is_decomposed_by = []
if element.IsDecomposedBy:
for rel in element.IsDecomposedBy:
is_decomposed_by.extend([e.id() for e in rel.RelatedObjects])
cls.spatial_elements[element.id()] = {
"Name": element.Name,
"LongName": element.LongName,
"Decomposes": decomposes,
"IsDecomposedBy": is_decomposed_by
}
@@ -3,12 +3,13 @@ import blenderbim.bim.module.spatial.assign_container as assign_container
import blenderbim.bim.module.spatial.remove_container as remove_container
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.spatial.data import Data
from blenderbim.bim.module.spatial.prop import getSpatialContainers
class AssignContainer(bpy.types.Operator):
bl_idname = "bim.assign_container"
bl_label = "Assign Container"
relating_structure: bpy.props.StringProperty()
relating_structure: bpy.props.IntProperty()
related_element: bpy.props.StringProperty()
def execute(self, context):
@@ -16,31 +17,37 @@ class AssignContainer(bpy.types.Operator):
related_element = (
bpy.data.objects.get(self.related_element) if self.related_element else bpy.context.active_object
)
props = related_element.BIMObjectProperties
oprops = related_element.BIMObjectProperties
sprops = context.scene.BIMSpatialProperties
props = related_element.BIMObjectSpatialProperties
relating_structure = (
bpy.data.objects.get(self.relating_structure) if self.relating_structure else props.relating_structure
self.relating_structure or sprops.spatial_elements[sprops.active_spatial_element_index].ifc_definition_id
)
if not relating_structure or not relating_structure.BIMObjectProperties.ifc_definition_id:
return {"FINISHED"}
assign_container.Usecase(
self.file,
{
"product": self.file.by_id(props.ifc_definition_id),
"relating_structure": self.file.by_id(relating_structure.BIMObjectProperties.ifc_definition_id),
"product": self.file.by_id(oprops.ifc_definition_id),
"relating_structure": self.file.by_id(relating_structure),
},
).execute()
bpy.ops.bim.edit_object_placement(obj=related_element.name)
Data.load(props.ifc_definition_id)
Data.load(oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_container(obj=related_element.name)
aggregate_collection = bpy.data.collections.get(related_element.name)
relating_collection = bpy.data.collections.get(relating_structure.name)
relating_structure_obj = IfcStore.id_map.get(relating_structure)
relating_collection = None
if relating_structure_obj:
relating_collection = bpy.data.collections.get(relating_structure_obj.name)
if aggregate_collection:
self.remove_collection(bpy.context.scene.collection, aggregate_collection)
for collection in bpy.data.collections:
self.remove_collection(collection, aggregate_collection)
relating_collection.children.link(aggregate_collection)
else:
elif relating_collection:
for collection in related_element.users_collection:
collection.objects.unlink(related_element)
relating_collection.objects.link(related_element)
@@ -58,8 +65,18 @@ class EnableEditingContainer(bpy.types.Operator):
bl_label = "Enable Editing Container"
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.relating_structure = None
bpy.context.active_object.BIMObjectProperties.is_editing_container = True
bpy.context.active_object.BIMObjectSpatialProperties.is_editing = True
getSpatialContainers(self, context)
return {"FINISHED"}
class ChangeSpatialLevel(bpy.types.Operator):
bl_idname = "bim.change_spatial_level"
bl_label = "Change Spatial Level"
parent_id: bpy.props.IntProperty()
def execute(self, context):
getSpatialContainers(self, context, parent_id=self.parent_id)
return {"FINISHED"}
@@ -70,7 +87,7 @@ class DisableEditingContainer(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
obj.BIMObjectProperties.is_editing_container = False
obj.BIMObjectSpatialProperties.is_editing = False
return {"FINISHED"}
@@ -81,10 +98,10 @@ class RemoveContainer(bpy.types.Operator):
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectProperties
oprops = obj.BIMObjectProperties
self.file = IfcStore.get_file()
remove_container.Usecase(self.file, {"product": self.file.by_id(props.ifc_definition_id)}).execute()
Data.load(props.ifc_definition_id)
remove_container.Usecase(self.file, {"product": self.file.by_id(oprops.ifc_definition_id)}).execute()
Data.load(oprops.ifc_definition_id)
aggregate_collection = bpy.data.collections.get(obj.name)
if aggregate_collection:
@@ -0,0 +1,67 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.spatial.data import Data
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
def getSpatialContainers(self, context, parent_id=None):
Data.load()
self.file = IfcStore.get_file()
props = context.scene.BIMSpatialProperties
while len(props.spatial_elements) > 0:
props.spatial_elements.remove(0)
if parent_id:
props.active_decomposes_id = parent_id
try:
self.file.by_id(props.active_decomposes_id)
except:
props.active_decomposes_id = 0
project_id = self.file.by_type("IfcProject")[0].id()
if not props.active_decomposes_id:
props.active_decomposes_id = project_id
for ifc_definition_id, spatial_element in Data.spatial_elements.items():
if spatial_element["Decomposes"] == props.active_decomposes_id:
new = props.spatial_elements.add()
new.name = spatial_element["Name"] or "Unnamed"
new.long_name = spatial_element["LongName"] or ""
new.has_decomposition = bool(spatial_element["IsDecomposedBy"])
new.ifc_definition_id = ifc_definition_id
if props.active_decomposes_id == project_id:
props.active_decomposes_parent_id = 0
else:
props.active_decomposes_parent_id = Data.spatial_elements[props.active_decomposes_id]["Decomposes"]
class SpatialElement(PropertyGroup):
name: StringProperty(name="Name")
long_name: StringProperty(name="Long Name")
has_decomposition: BoolProperty(name="Has Decomposition")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMSpatialProperties(PropertyGroup):
spatial_elements: CollectionProperty(name="Spatial Elements", type=SpatialElement)
active_spatial_element_index: IntProperty(name="Active Spatial Element Index")
active_decomposes_id: IntProperty(name="Active Decomposes Id")
active_decomposes_parent_id: IntProperty(name="Active Decomposes Parent Id")
class BIMObjectSpatialProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
@@ -1,4 +1,4 @@
from bpy.types import Panel
from bpy.types import Panel, UIList
from blenderbim.bim.module.spatial.data import Data
@@ -13,34 +13,52 @@ class BIM_PT_spatial(Panel):
def poll(cls, context):
if not context.active_object:
return False
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
oprops = context.active_object.BIMObjectProperties
if not oprops.ifc_definition_id:
return False
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
if oprops.ifc_definition_id not in Data.products:
Data.load(oprops.ifc_definition_id)
if not Data.products[oprops.ifc_definition_id]:
return False
return True
def draw(self, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
oprops = context.active_object.BIMObjectProperties
sprops = context.scene.BIMSpatialProperties
props = context.active_object.BIMObjectSpatialProperties
if not oprops.ifc_definition_id:
return
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
if oprops.ifc_definition_id not in Data.products:
Data.load(oprops.ifc_definition_id)
if props.is_editing_container:
if props.is_editing:
row = self.layout.row(align=True)
row.prop(props, "relating_structure", text="")
row.operator("bim.assign_container", icon="CHECKMARK", text="")
if sprops.active_decomposes_parent_id:
op = row.operator("bim.change_spatial_level", text="", icon="FRAME_PREV")
op.parent_id = sprops.active_decomposes_parent_id
row.operator("bim.assign_container", icon="CHECKMARK")
row.operator("bim.disable_editing_container", icon="X", text="")
self.layout.template_list(
"BIM_UL_spatial_elements", "", sprops, "spatial_elements", sprops, "active_spatial_element_index"
)
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
Data.products[oprops.ifc_definition_id]["type"], Data.products[oprops.ifc_definition_id]["Name"]
)
if name == "None/None":
name = "This object is not spatially contained"
row.label(text=name)
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
row.operator("bim.remove_container", icon="X", text="")
class BIM_UL_spatial_elements(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
if item.has_decomposition:
op = layout.operator("bim.change_spatial_level", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
op.parent_id = item.ifc_definition_id
layout.label(text=item.name)
layout.label(text=item.long_name)
@@ -498,8 +498,6 @@ class BIMObjectProperties(PropertyGroup):
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
is_editing_aggregate: BoolProperty(name="Is Editing Aggregate")
is_editing_container: BoolProperty(name="Is Editing Container")
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
psets: CollectionProperty(name="Psets", type=PsetQto)
qtos: CollectionProperty(name="Qtos", type=PsetQto)
has_boundary_condition: BoolProperty(name="Has Boundary Condition")