mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 14:33:28 +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:
|
if self.props.is_editing:
|
||||||
self.layout.template_list(
|
self.layout.template_list(
|
||||||
"BIM_UL_documents",
|
"BIM_UL_documents", "", self.props, "documents", self.props, "active_document_index"
|
||||||
"",
|
|
||||||
self.props,
|
|
||||||
"documents",
|
|
||||||
self.props,
|
|
||||||
"active_document_index",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.props.active_document_id:
|
if self.props.active_document_id:
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ class UnassignClass(bpy.types.Operator):
|
|||||||
self.remove_representations(obj, product)
|
self.remove_representations(obj, product)
|
||||||
IfcStore.unlink_element(product, obj)
|
IfcStore.unlink_element(product, obj)
|
||||||
remove_product.Usecase(self.file, {"product": product}).execute()
|
remove_product.Usecase(self.file, {"product": product}).execute()
|
||||||
|
# TODO: remove object placement
|
||||||
if "/" in obj.name and obj.name[0:3] == "Ifc":
|
if "/" in obj.name and obj.name[0:3] == "Ifc":
|
||||||
obj.name = "/".join(obj.name.split("/")[1:])
|
obj.name = "/".join(obj.name.split("/")[1:])
|
||||||
if obj.data and obj.data.name == "Void":
|
if obj.data and obj.data.name == "Void":
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import ui, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.AssignContainer,
|
operator.AssignContainer,
|
||||||
operator.RemoveContainer,
|
operator.RemoveContainer,
|
||||||
operator.EnableEditingContainer,
|
operator.EnableEditingContainer,
|
||||||
operator.DisableEditingContainer,
|
operator.DisableEditingContainer,
|
||||||
|
operator.ChangeSpatialLevel,
|
||||||
|
prop.SpatialElement,
|
||||||
|
prop.BIMSpatialProperties,
|
||||||
|
prop.BIMObjectSpatialProperties,
|
||||||
ui.BIM_PT_spatial,
|
ui.BIM_PT_spatial,
|
||||||
|
ui.BIM_UL_spatial_elements,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
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():
|
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:
|
class Data:
|
||||||
products = {}
|
products = {}
|
||||||
|
spatial_elements = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def purge(cls):
|
def purge(cls):
|
||||||
cls.products = {}
|
cls.products = {}
|
||||||
|
cls.spatial_elements = {}
|
||||||
|
|
||||||
@classmethod
|
@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()
|
file = IfcStore.get_file()
|
||||||
if not file:
|
if not file:
|
||||||
return
|
return
|
||||||
@@ -21,3 +29,27 @@ class Data:
|
|||||||
cls.products[product_id] = {"type": structure.is_a(), "Name": structure.Name, "id": int(structure.id())}
|
cls.products[product_id] = {"type": structure.is_a(), "Name": structure.Name, "id": int(structure.id())}
|
||||||
else:
|
else:
|
||||||
cls.products[product_id] = {"type": None, "Name": None, "id": None}
|
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
|
import blenderbim.bim.module.spatial.remove_container as remove_container
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.spatial.data import Data
|
from blenderbim.bim.module.spatial.data import Data
|
||||||
|
from blenderbim.bim.module.spatial.prop import getSpatialContainers
|
||||||
|
|
||||||
|
|
||||||
class AssignContainer(bpy.types.Operator):
|
class AssignContainer(bpy.types.Operator):
|
||||||
bl_idname = "bim.assign_container"
|
bl_idname = "bim.assign_container"
|
||||||
bl_label = "Assign Container"
|
bl_label = "Assign Container"
|
||||||
relating_structure: bpy.props.StringProperty()
|
relating_structure: bpy.props.IntProperty()
|
||||||
related_element: bpy.props.StringProperty()
|
related_element: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -16,31 +17,37 @@ class AssignContainer(bpy.types.Operator):
|
|||||||
related_element = (
|
related_element = (
|
||||||
bpy.data.objects.get(self.related_element) if self.related_element else bpy.context.active_object
|
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 = (
|
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(
|
assign_container.Usecase(
|
||||||
self.file,
|
self.file,
|
||||||
{
|
{
|
||||||
"product": self.file.by_id(props.ifc_definition_id),
|
"product": self.file.by_id(oprops.ifc_definition_id),
|
||||||
"relating_structure": self.file.by_id(relating_structure.BIMObjectProperties.ifc_definition_id),
|
"relating_structure": self.file.by_id(relating_structure),
|
||||||
},
|
},
|
||||||
).execute()
|
).execute()
|
||||||
bpy.ops.bim.edit_object_placement(obj=related_element.name)
|
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)
|
bpy.ops.bim.disable_editing_container(obj=related_element.name)
|
||||||
|
|
||||||
aggregate_collection = bpy.data.collections.get(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:
|
if aggregate_collection:
|
||||||
self.remove_collection(bpy.context.scene.collection, aggregate_collection)
|
self.remove_collection(bpy.context.scene.collection, aggregate_collection)
|
||||||
for collection in bpy.data.collections:
|
for collection in bpy.data.collections:
|
||||||
self.remove_collection(collection, aggregate_collection)
|
self.remove_collection(collection, aggregate_collection)
|
||||||
relating_collection.children.link(aggregate_collection)
|
relating_collection.children.link(aggregate_collection)
|
||||||
else:
|
elif relating_collection:
|
||||||
for collection in related_element.users_collection:
|
for collection in related_element.users_collection:
|
||||||
collection.objects.unlink(related_element)
|
collection.objects.unlink(related_element)
|
||||||
relating_collection.objects.link(related_element)
|
relating_collection.objects.link(related_element)
|
||||||
@@ -58,8 +65,18 @@ class EnableEditingContainer(bpy.types.Operator):
|
|||||||
bl_label = "Enable Editing Container"
|
bl_label = "Enable Editing Container"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.context.active_object.BIMObjectProperties.relating_structure = None
|
bpy.context.active_object.BIMObjectSpatialProperties.is_editing = True
|
||||||
bpy.context.active_object.BIMObjectProperties.is_editing_container = 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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +87,7 @@ class DisableEditingContainer(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -81,10 +98,10 @@ class RemoveContainer(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
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()
|
self.file = IfcStore.get_file()
|
||||||
remove_container.Usecase(self.file, {"product": self.file.by_id(props.ifc_definition_id)}).execute()
|
remove_container.Usecase(self.file, {"product": self.file.by_id(oprops.ifc_definition_id)}).execute()
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(oprops.ifc_definition_id)
|
||||||
|
|
||||||
aggregate_collection = bpy.data.collections.get(obj.name)
|
aggregate_collection = bpy.data.collections.get(obj.name)
|
||||||
if aggregate_collection:
|
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
|
from blenderbim.bim.module.spatial.data import Data
|
||||||
|
|
||||||
|
|
||||||
@@ -13,34 +13,52 @@ class BIM_PT_spatial(Panel):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not context.active_object:
|
if not context.active_object:
|
||||||
return False
|
return False
|
||||||
props = context.active_object.BIMObjectProperties
|
oprops = context.active_object.BIMObjectProperties
|
||||||
if not props.ifc_definition_id:
|
if not oprops.ifc_definition_id:
|
||||||
return False
|
return False
|
||||||
if props.ifc_definition_id not in Data.products:
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(oprops.ifc_definition_id)
|
||||||
if not Data.products[props.ifc_definition_id]:
|
if not Data.products[oprops.ifc_definition_id]:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
props = context.active_object.BIMObjectProperties
|
oprops = context.active_object.BIMObjectProperties
|
||||||
if not props.ifc_definition_id:
|
sprops = context.scene.BIMSpatialProperties
|
||||||
|
props = context.active_object.BIMObjectSpatialProperties
|
||||||
|
if not oprops.ifc_definition_id:
|
||||||
return
|
return
|
||||||
if props.ifc_definition_id not in Data.products:
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(oprops.ifc_definition_id)
|
||||||
|
|
||||||
if props.is_editing_container:
|
if props.is_editing:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "relating_structure", text="")
|
if sprops.active_decomposes_parent_id:
|
||||||
row.operator("bim.assign_container", icon="CHECKMARK", text="")
|
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="")
|
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:
|
else:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
name = "{}/{}".format(
|
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":
|
if name == "None/None":
|
||||||
name = "This object is not spatially contained"
|
name = "This object is not spatially contained"
|
||||||
row.label(text=name)
|
row.label(text=name)
|
||||||
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
||||||
row.operator("bim.remove_container", icon="X", 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)
|
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
|
||||||
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
|
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
|
||||||
is_editing_aggregate: BoolProperty(name="Is Editing Aggregate")
|
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)
|
psets: CollectionProperty(name="Psets", type=PsetQto)
|
||||||
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
||||||
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
||||||
|
|||||||
Reference in New Issue
Block a user