mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Improve UI to change spatial container to prevent accidentally selecting incorrect objects
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user