Deprecate "duplicate" spatial tree browser in favour of regular object selection

This commit is contained in:
Dion Moult
2024-07-29 22:29:36 +10:00
parent f8848051ca
commit a9a4b0ccf0
7 changed files with 60 additions and 130 deletions
@@ -21,7 +21,6 @@ from . import ui, prop, operator, workspace
classes = (
operator.AssignContainer,
operator.ChangeSpatialLevel,
operator.ContractContainer,
operator.CopyToContainer,
operator.DeleteContainer,
@@ -39,14 +38,11 @@ classes = (
operator.SelectSimilarContainer,
operator.SetContainerVisibility,
operator.SetDefaultContainer,
prop.SpatialElement,
prop.Element,
prop.BIMSpatialProperties,
prop.BIMObjectSpatialProperties,
prop.BIMContainer,
prop.BIMSpatialDecompositionProperties,
ui.BIM_PT_spatial,
ui.BIM_UL_containers,
ui.BIM_UL_containers_manager,
ui.BIM_UL_elements,
ui.BIM_PT_spatial_decomposition,
@@ -57,7 +53,6 @@ classes = (
def register():
if not bpy.app.background:
bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False)
bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties)
bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
bpy.types.Scene.BIMSpatialDecompositionProperties = bpy.props.PointerProperty(
type=prop.BIMSpatialDecompositionProperties
@@ -67,6 +62,5 @@ def register():
def unregister():
if not bpy.app.background:
bpy.utils.unregister_tool(workspace.SpatialTool)
del bpy.types.Scene.BIMSpatialProperties
del bpy.types.Object.BIMObjectSpatialProperties
del bpy.types.Scene.BIMSpatialDecompositionProperties
@@ -37,7 +37,8 @@ class SpatialData:
if cls.data["poll"]:
cls.data.update(
{
"parent_container_id": cls.parent_container_id(),
"default_container": cls.default_container(),
"selected_containers": cls.selected_containers(),
"is_directly_contained": cls.is_directly_contained(),
"label": cls.label(),
"references": cls.references(),
@@ -56,14 +57,17 @@ class SpatialData:
return False
@classmethod
def parent_container_id(cls):
container_id = bpy.context.scene.BIMSpatialProperties.active_container_id
if not container_id:
return
container = tool.Ifc.get_entity_by_id(container_id)
if not container or container.is_a("IfcProject"):
return
return container.Decomposes[0].RelatingObject.id()
def default_container(cls) -> str | None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
if props.default_container:
try:
return tool.Ifc.get().by_id(props.default_container).Name
except:
pass
@classmethod
def selected_containers(cls):
return sorted([e.Name or "Unnamed" for e in tool.Spatial.get_selected_containers()])
@classmethod
def label(cls):
@@ -30,11 +30,9 @@ class ReferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.reference_structure"
bl_label = "Reference Structure"
bl_options = {"REGISTER", "UNDO"}
structure: bpy.props.IntProperty()
def _execute(self, context):
sprops = context.scene.BIMSpatialProperties
containers = [tool.Ifc.get().by_id(c.ifc_definition_id) for c in sprops.containers if c.is_selected]
containers = tool.Spatial.get_selected_containers()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
@@ -47,11 +45,9 @@ class DereferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.dereference_structure"
bl_label = "Dereference Structure"
bl_options = {"REGISTER", "UNDO"}
structure: bpy.props.IntProperty()
def _execute(self, context):
sprops = context.scene.BIMSpatialProperties
containers = [tool.Ifc.get().by_id(c.ifc_definition_id) for c in sprops.containers if c.is_selected]
containers = tool.Spatial.get_selected_containers()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
@@ -64,14 +60,13 @@ class AssignContainer(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_container"
bl_label = "Assign Container"
bl_options = {"REGISTER", "UNDO"}
structure: bpy.props.IntProperty()
def _execute(self, context):
structure_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.structure))
for element_obj in context.selected_objects:
core.assign_container(
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=structure_obj, element_obj=element_obj
)
if container := tool.Root.get_default_container():
for element_obj in context.selected_objects:
core.assign_container(
tool.Ifc, tool.Collector, tool.Spatial, container=container, element_obj=element_obj
)
class EnableEditingContainer(bpy.types.Operator, tool.Ifc.Operator):
@@ -83,16 +78,6 @@ class EnableEditingContainer(bpy.types.Operator, tool.Ifc.Operator):
core.enable_editing_container(tool.Spatial, obj=context.active_object)
class ChangeSpatialLevel(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.change_spatial_level"
bl_label = "Change Spatial Level"
bl_options = {"REGISTER", "UNDO"}
parent: bpy.props.IntProperty()
def _execute(self, context):
core.change_spatial_level(tool.Spatial, parent=tool.Ifc.get().by_id(self.parent))
class DisableEditingContainer(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_container"
bl_label = "Disable Editing Container"
@@ -129,11 +114,10 @@ class CopyToContainer(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
sprops = context.scene.BIMSpatialProperties
# Track decompositions so they can be recreated after the operation
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
old_to_new = {}
containers = [tool.Ifc.get().by_id(c.ifc_definition_id) for c in sprops.containers if c.is_selected]
containers = tool.Spatial.get_selected_containers()
for obj in context.selected_objects:
result_objs = core.copy_to_container(tool.Ifc, tool.Collector, tool.Spatial, obj=obj, containers=containers)
if result_objs:
@@ -82,20 +82,6 @@ def is_object_applicable(self, obj):
return bool(element)
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")
is_selected: BoolProperty(name="Is Selected")
class BIMSpatialProperties(PropertyGroup):
containers: CollectionProperty(name="Containers", type=SpatialElement)
active_container_index: IntProperty(name="Active Container Index")
active_container_id: IntProperty(name="Active Container Id")
class BIMObjectSpatialProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing")
relating_container_object: PointerProperty(
@@ -39,24 +39,32 @@ class BIM_PT_spatial(Panel):
if not SpatialData.is_loaded:
SpatialData.load()
props = context.scene.BIMSpatialProperties
osprops = context.active_object.BIMObjectSpatialProperties
if osprops.is_editing:
row = self.layout.row(align=True)
if SpatialData.data["parent_container_id"]:
op = row.operator("bim.change_spatial_level", text="", icon="FRAME_PREV")
op.parent = SpatialData.data["parent_container_id"]
if props.containers and props.active_container_index < len(props.containers):
op = row.operator("bim.assign_container", icon="CHECKMARK")
op.structure = props.containers[props.active_container_index].ifc_definition_id
row.operator("bim.reference_structure", icon="LINKED", text="")
row.operator("bim.dereference_structure", icon="UNLINKED", text="")
row.operator("bim.copy_to_container", icon="COPYDOWN", text="")
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
if SpatialData.data["default_container"]:
row = self.layout.row(align=True)
row.label(text=f"Target: {SpatialData.data['default_container']}", icon="OUTLINER_COLLECTION")
row.operator("bim.assign_container", icon="CHECKMARK", text="Reassign Container")
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
self.layout.template_list("BIM_UL_containers", "", props, "containers", props, "active_container_index")
self.layout.prop(osprops, "relating_container_object")
if SpatialData.data["selected_containers"]:
row = self.layout.row()
row.label(text=f"{len(SpatialData.data['selected_containers'])} Selected Containers")
for name in SpatialData.data['selected_containers'][:3]:
row = self.layout.row()
row.label(text=name, icon="OUTLINER_COLLECTION")
if len(SpatialData.data['selected_containers']) > 3:
row = self.layout.row()
row.label(text=f"... {len(SpatialData.data['selected_containers']) - 3} More")
row = self.layout.row(align=True)
row.operator("bim.reference_structure", icon="LINKED", text="Reference Selected")
row.operator("bim.dereference_structure", icon="UNLINKED", text="")
row = self.layout.row()
row.operator("bim.copy_to_container", icon="COPYDOWN", text="Copy Object To Selected")
else:
row = self.layout.row()
row.label(text="No Selected Containers")
else:
row = self.layout.row(align=True)
if SpatialData.data["label"]:
@@ -74,23 +82,6 @@ class BIM_PT_spatial(Panel):
row.label(text=reference, icon="LINKED")
class BIM_UL_containers(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 = item.ifc_definition_id
layout.label(text=item.name)
layout.label(text=item.long_name)
layout.prop(
item,
"is_selected",
icon="CHECKBOX_HLT" if item.is_selected else "CHECKBOX_DEHLT",
text="",
emboss=False,
)
class BIM_PT_spatial_decomposition(Panel):
bl_label = "Spatial Decomposition"
bl_idname = "BIM_PT_spatial_decomposition"
+4 -13
View File
@@ -49,17 +49,13 @@ def assign_container(
ifc: tool.Ifc,
collector: tool.Collector,
spatial: tool.Spatial,
structure_obj: Optional[bpy.types.Object] = None,
container: ifcopenshell.entity_instance,
element_obj: Optional[bpy.types.Object] = None,
) -> Union[ifcopenshell.entity_instance, None]:
if not spatial.can_contain(structure_obj, element_obj):
if not spatial.can_contain(container, element_obj):
return
assert element_obj and structure_obj # Type checker.
rel = ifc.run(
"spatial.assign_container",
products=[ifc.get_entity(element_obj)],
relating_structure=ifc.get_entity(structure_obj),
)
assert element_obj # Type checker.
rel = ifc.run("spatial.assign_container", products=[ifc.get_entity(element_obj)], relating_structure=container)
spatial.disable_editing(element_obj)
collector.assign(element_obj)
return rel
@@ -67,17 +63,12 @@ def assign_container(
def enable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None:
spatial.enable_editing(obj)
spatial.import_containers()
def disable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None:
spatial.disable_editing(obj)
def change_spatial_level(spatial: tool.Spatial, parent: Union[ifcopenshell.entity_instance, None] = None) -> None:
spatial.import_containers(parent=parent)
def remove_container(ifc: tool.Ifc, collector: tool.Collector, obj: bpy.types.Object) -> None:
ifc.run("spatial.unassign_container", products=[ifc.get_entity(obj)])
collector.assign(obj)
+13 -33
View File
@@ -39,23 +39,20 @@ import json
from math import pi
from mathutils import Vector, Matrix
from shapely import Polygon
from typing import Generator, Optional, Union, Literal, Any, List
from typing import Generator, Optional, Union, Literal, List
class Spatial(blenderbim.core.tool.Spatial):
@classmethod
def can_contain(
cls, structure_obj: Union[bpy.types.Object, None], element_obj: Union[bpy.types.Object, None]
) -> bool:
structure = tool.Ifc.get_entity(structure_obj)
def can_contain(cls, container: ifcopenshell.entity_instance, element_obj: Union[bpy.types.Object, None]) -> bool:
element = tool.Ifc.get_entity(element_obj)
if not structure or not element:
if not element:
return False
if tool.Ifc.get_schema() == "IFC2X3":
if not structure.is_a("IfcSpatialStructureElement"):
if not container.is_a("IfcSpatialStructureElement"):
return False
else:
if not structure.is_a("IfcSpatialStructureElement") and not structure.is_a(
if not container.is_a("IfcSpatialStructureElement") and not container.is_a(
"IfcExternalSpatialStructureElement"
):
return False
@@ -111,31 +108,6 @@ class Spatial(blenderbim.core.tool.Spatial):
def get_relative_object_matrix(cls, target_obj: bpy.types.Object, relative_to_obj: bpy.types.Object) -> Matrix:
return relative_to_obj.matrix_world.inverted() @ target_obj.matrix_world
@classmethod
def import_containers(cls, parent: Optional[ifcopenshell.entity_instance] = None) -> None:
props = bpy.context.scene.BIMSpatialProperties
props.containers.clear()
if not parent:
parent = tool.Ifc.get().by_type("IfcProject")[0]
props.active_container_id = parent.id()
for rel in parent.IsDecomposedBy or []:
related_objects = []
for element in rel.RelatedObjects:
# skip objects without placements
if not element.is_a("IfcProduct"):
continue
related_objects.append((element, ifcopenshell.util.placement.get_storey_elevation(element)))
related_objects = sorted(related_objects, key=lambda e: e[1])
for element, _ in related_objects:
new = 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()
@classmethod
def run_root_copy_class(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance:
return blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
@@ -886,3 +858,11 @@ class Spatial(blenderbim.core.tool.Spatial):
if subelement:
return subelement
return None
@classmethod
def get_selected_containers(cls) -> List[ifcopenshell.entity_instance]:
results = []
for obj in tool.Blender.get_selected_objects():
if (element := tool.Ifc.get_entity(obj)) and tool.Root.is_spatial_element(element):
results.append(element)
return results