mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Make referencing in structure more discoverable #6514
Didn't wanted to disrupt existing operators, but we probably should deprecate them.
This commit is contained in:
@@ -24,11 +24,13 @@ classes = (
|
||||
operator.ContractContainer,
|
||||
operator.CopyToContainer,
|
||||
operator.DeleteContainer,
|
||||
operator.DereferenceFromProvidedStructure,
|
||||
operator.DereferenceStructure,
|
||||
operator.DisableEditingContainer,
|
||||
operator.EnableEditingContainer,
|
||||
operator.ExpandContainer,
|
||||
operator.ImportSpatialDecomposition,
|
||||
operator.ReferenceFromProvidedStructure,
|
||||
operator.ReferenceStructure,
|
||||
operator.RemoveContainer,
|
||||
operator.SelectContainer,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from typing import Any
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -79,9 +80,20 @@ class SpatialData:
|
||||
return label
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
results = ifcopenshell.util.element.get_referenced_structures(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
return sorted([f"{r.is_a()}/{r.Name or ''}" for r in results])
|
||||
def references(cls) -> list[dict[str, Any]]:
|
||||
assert (obj := bpy.context.active_object) and (element := tool.Ifc.get_entity(obj))
|
||||
results: list[dict[str, Any]] = []
|
||||
for structure in ifcopenshell.util.element.get_referenced_structures(element):
|
||||
ifc_class = structure.is_a()
|
||||
results.append(
|
||||
{
|
||||
"id": structure.id(),
|
||||
"name": f"{ifc_class}/{structure.Name or ''}",
|
||||
"type": ifc_class,
|
||||
}
|
||||
)
|
||||
results.sort(key=lambda x: x["name"])
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def is_directly_contained(cls):
|
||||
|
||||
@@ -73,6 +73,76 @@ class DereferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.dereference_structure(tool.Ifc, tool.Spatial, structure=container, element=element)
|
||||
|
||||
|
||||
class ReferenceFromProvidedStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.reference_from_provided_structure"
|
||||
bl_label = "Reference from Provided Structure"
|
||||
bl_description = "Reference selected objects from the provided structure.\n\n" "ALT + Click to dereference instead."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
structure: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
dereference: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not tool.Blender.get_selected_objects():
|
||||
cls.poll_message_set("No objects selected.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.dereference = event.alt
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
structure = ifc_file.by_id(self.structure)
|
||||
|
||||
objs = tool.Spatial.get_selected_objects_without_containers()
|
||||
if not objs:
|
||||
self.report({"INFO"}, "No non-spatial objects are selected.")
|
||||
return
|
||||
|
||||
elements = [e for o in objs if (e := tool.Ifc.get_entity(o))]
|
||||
for element in elements:
|
||||
if self.dereference:
|
||||
core.dereference_structure(tool.Ifc, tool.Spatial, structure=structure, element=element)
|
||||
else:
|
||||
core.reference_structure(tool.Ifc, tool.Spatial, structure=structure, element=element)
|
||||
|
||||
msg = "dereferenced" if self.dereference else "referenced"
|
||||
self.report({"INFO"}, f"{len(elements)} elements {msg} from the structure.")
|
||||
|
||||
|
||||
class DereferenceFromProvidedStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.dereference_from_provided_structure"
|
||||
bl_label = "Dereference from Provided Structure"
|
||||
bl_description = "Dereference selected objects from the provided structure."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
structure: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not tool.Blender.get_selected_objects():
|
||||
cls.poll_message_set("No objects selected.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
structure = ifc_file.by_id(self.structure)
|
||||
objs = tool.Spatial.get_selected_objects_without_containers()
|
||||
if not objs:
|
||||
self.report({"INFO"}, "No non-spatial objects are selected.")
|
||||
return
|
||||
|
||||
elements = [e for o in objs if (e := tool.Ifc.get_entity(o))]
|
||||
for element in elements:
|
||||
core.dereference_structure(tool.Ifc, tool.Spatial, structure=structure, element=element)
|
||||
|
||||
self.report({"INFO"}, f"{len(elements)} elements dereferenced from the structure.")
|
||||
|
||||
|
||||
class AssignContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_container"
|
||||
bl_label = "Assign Container"
|
||||
|
||||
@@ -21,7 +21,7 @@ import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.module.spatial.data import SpatialData, SpatialDecompositionData
|
||||
import bonsai.tool as tool
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, cast, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.spatial.prop import BIMSpatialDecompositionProperties, BIMContainer, Element
|
||||
@@ -56,6 +56,8 @@ class BIM_PT_spatial(Panel):
|
||||
row.operator("bim.assign_container", icon="CHECKMARK", text="")
|
||||
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
|
||||
|
||||
# TODO: deprecate as it's very hard to discover
|
||||
# containers are not even selectable by default.
|
||||
if SpatialData.data["selected_containers"]:
|
||||
row = self.layout.row()
|
||||
row.label(text=f"{len(SpatialData.data['selected_containers'])} Selected Containers")
|
||||
@@ -86,14 +88,16 @@ class BIM_PT_spatial(Panel):
|
||||
row.label(text="No Spatial Container")
|
||||
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
||||
|
||||
if references := SpatialData.data["references"]:
|
||||
if references := cast(list[dict[str, Any]], SpatialData.data["references"]):
|
||||
self.layout.label(text=f"{len(references)} References:")
|
||||
else:
|
||||
self.layout.label(text="No References Found")
|
||||
|
||||
for reference in references:
|
||||
row = self.layout.row()
|
||||
row.label(text=reference, icon="LINKED")
|
||||
row.label(text=reference["name"], icon="LINKED")
|
||||
op = row.operator("bim.dereference_from_provided_structure", icon="X", text="")
|
||||
op.structure = reference["id"]
|
||||
|
||||
|
||||
class BIM_PT_spatial_decomposition(Panel):
|
||||
@@ -200,6 +204,7 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
op.container = ifc_definition_id
|
||||
|
||||
row.operator("bim.assign_container", icon="FOLDER_REDIRECT", text="").container = ifc_definition_id
|
||||
row.operator("bim.reference_from_provided_structure", icon="LINKED", text="").structure = ifc_definition_id
|
||||
op = row.operator("bim.select_decomposed_element", icon="OBJECT_DATA", text="")
|
||||
if active_element := self.props.active_element:
|
||||
op.element = active_element.ifc_definition_id
|
||||
|
||||
Reference in New Issue
Block a user