mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 10:51:13 +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.ContractContainer,
|
||||||
operator.CopyToContainer,
|
operator.CopyToContainer,
|
||||||
operator.DeleteContainer,
|
operator.DeleteContainer,
|
||||||
|
operator.DereferenceFromProvidedStructure,
|
||||||
operator.DereferenceStructure,
|
operator.DereferenceStructure,
|
||||||
operator.DisableEditingContainer,
|
operator.DisableEditingContainer,
|
||||||
operator.EnableEditingContainer,
|
operator.EnableEditingContainer,
|
||||||
operator.ExpandContainer,
|
operator.ExpandContainer,
|
||||||
operator.ImportSpatialDecomposition,
|
operator.ImportSpatialDecomposition,
|
||||||
|
operator.ReferenceFromProvidedStructure,
|
||||||
operator.ReferenceStructure,
|
operator.ReferenceStructure,
|
||||||
operator.RemoveContainer,
|
operator.RemoveContainer,
|
||||||
operator.SelectContainer,
|
operator.SelectContainer,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -79,9 +80,20 @@ class SpatialData:
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def references(cls):
|
def references(cls) -> list[dict[str, Any]]:
|
||||||
results = ifcopenshell.util.element.get_referenced_structures(tool.Ifc.get_entity(bpy.context.active_object))
|
assert (obj := bpy.context.active_object) and (element := tool.Ifc.get_entity(obj))
|
||||||
return sorted([f"{r.is_a()}/{r.Name or ''}" for r in results])
|
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
|
@classmethod
|
||||||
def is_directly_contained(cls):
|
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)
|
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):
|
class AssignContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.assign_container"
|
bl_idname = "bim.assign_container"
|
||||||
bl_label = "Assign Container"
|
bl_label = "Assign Container"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import bpy
|
|||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from bonsai.bim.module.spatial.data import SpatialData, SpatialDecompositionData
|
from bonsai.bim.module.spatial.data import SpatialData, SpatialDecompositionData
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, cast, Any
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.spatial.prop import BIMSpatialDecompositionProperties, BIMContainer, Element
|
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.assign_container", icon="CHECKMARK", text="")
|
||||||
row.operator("bim.disable_editing_container", icon="CANCEL", 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"]:
|
if SpatialData.data["selected_containers"]:
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.label(text=f"{len(SpatialData.data['selected_containers'])} Selected Containers")
|
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.label(text="No Spatial Container")
|
||||||
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
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:")
|
self.layout.label(text=f"{len(references)} References:")
|
||||||
else:
|
else:
|
||||||
self.layout.label(text="No References Found")
|
self.layout.label(text="No References Found")
|
||||||
|
|
||||||
for reference in references:
|
for reference in references:
|
||||||
row = self.layout.row()
|
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):
|
class BIM_PT_spatial_decomposition(Panel):
|
||||||
@@ -200,6 +204,7 @@ class BIM_PT_spatial_decomposition(Panel):
|
|||||||
op.container = ifc_definition_id
|
op.container = ifc_definition_id
|
||||||
|
|
||||||
row.operator("bim.assign_container", icon="FOLDER_REDIRECT", text="").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="")
|
op = row.operator("bim.select_decomposed_element", icon="OBJECT_DATA", text="")
|
||||||
if active_element := self.props.active_element:
|
if active_element := self.props.active_element:
|
||||||
op.element = active_element.ifc_definition_id
|
op.element = active_element.ifc_definition_id
|
||||||
|
|||||||
Reference in New Issue
Block a user