diff --git a/src/blenderbim/blenderbim/bim/module/boundary/__init__.py b/src/blenderbim/blenderbim/bim/module/boundary/__init__.py
index 4a18622060..22944a5bfa 100644
--- a/src/blenderbim/blenderbim/bim/module/boundary/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/boundary/__init__.py
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see .
import bpy
-from . import ui, operator
+from . import ui, operator, prop
classes = (
operator.LoadProjectSpaceBoundaries,
@@ -25,14 +25,19 @@ classes = (
operator.LoadBoundary,
operator.SelectProjectBoundaries,
operator.ColourByRelatedBuildingElement,
+ operator.EnableEditingBoundary,
+ operator.DisableEditingBoundary,
+ operator.EditBoundaryAttributes,
ui.BIM_PT_Boundary,
+ ui.BIM_PT_SpaceBoundaries,
ui.BIM_PT_SceneBoundaries,
+ prop.BIMBoundaryProperties,
)
def register():
- pass
+ bpy.types.Object.bim_boundary_properties = bpy.props.PointerProperty(type=prop.BIMBoundaryProperties)
def unregister():
- pass
+ del bpy.types.Object.bim_boundary_properties
diff --git a/src/blenderbim/blenderbim/bim/module/boundary/operator.py b/src/blenderbim/blenderbim/bim/module/boundary/operator.py
index 4be2cff3e3..b9320937cb 100644
--- a/src/blenderbim/blenderbim/bim/module/boundary/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/boundary/operator.py
@@ -202,3 +202,64 @@ class ColourByRelatedBuildingElement(bpy.types.Operator):
def commit(self, data):
if data:
data["area"].spaces[0].shading.color_type = "OBJECT"
+
+
+EDITABLE_ATTRIBUTES = {
+ "RelatingSpace": "relating_space",
+ "RelatedBuildingElement": "related_building_element",
+ "ParentBoundary": "parent_boundary",
+ "CorrespondingBoundary": "corresponding_boundary",
+}
+
+
+class EnableEditingBoundary(bpy.types.Operator):
+ bl_idname = "bim.enable_editing_boundary"
+ bl_label = "Edit boundary relations"
+ bl_options = {"REGISTER", "UNDO"}
+
+ def execute(self, context):
+ bprops = context.active_object.bim_boundary_properties
+ bprops.is_editing = True
+ boundary = tool.Ifc.get_entity(context.active_object)
+ for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
+ entity = getattr(boundary, ifc_attribute, None)
+ if not entity:
+ continue
+ obj = tool.Ifc.get_object(entity)
+ if entity and obj:
+ setattr(bprops, blender_property, obj)
+ return {"FINISHED"}
+
+
+class DisableEditingBoundary(bpy.types.Operator):
+ bl_idname = "bim.disable_editing_boundary"
+ bl_label = "Disable editing boundary relations"
+ bl_options = {"REGISTER", "UNDO"}
+
+ def execute(self, context):
+ bprops = context.active_object.bim_boundary_properties
+ bprops.is_editing = False
+ for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
+ setattr(bprops, blender_property, None)
+ return {"FINISHED"}
+
+
+class EditBoundaryAttributes(bpy.types.Operator):
+ bl_idname = "bim.edit_boundary_attributes"
+ bl_label = "Disable editing boundary relations"
+ bl_options = {"REGISTER", "UNDO"}
+
+ def execute(self, context):
+ return IfcStore.execute_ifc_operator(self, context)
+
+ def _execute(self, context):
+ bprops = context.active_object.bim_boundary_properties
+ boundary = tool.Ifc.get_entity(context.active_object)
+ for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
+ if not hasattr(boundary, ifc_attribute):
+ continue
+ obj = getattr(bprops, blender_property, None)
+ entity = tool.Ifc.get_entity(obj)
+ setattr(boundary, ifc_attribute, entity)
+ bpy.ops.bim.disable_editing_boundary()
+ return {"FINISHED"}
diff --git a/src/blenderbim/blenderbim/bim/module/boundary/prop.py b/src/blenderbim/blenderbim/bim/module/boundary/prop.py
new file mode 100644
index 0000000000..e42d414e33
--- /dev/null
+++ b/src/blenderbim/blenderbim/bim/module/boundary/prop.py
@@ -0,0 +1,60 @@
+# BlenderBIM Add-on - OpenBIM Blender Add-on
+# Copyright (C) 2021 Dion Moult
+#
+# This file is part of BlenderBIM Add-on.
+#
+# BlenderBIM Add-on is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BlenderBIM Add-on is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BlenderBIM Add-on. If not, see .
+
+import bpy
+from bpy.types import PropertyGroup
+from bpy.props import (
+ PointerProperty,
+ StringProperty,
+ EnumProperty,
+ BoolProperty,
+ IntProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ CollectionProperty,
+)
+import blenderbim.tool as tool
+
+
+def space_filter(self, object):
+ entity = tool.Ifc.get_entity(object)
+ if entity:
+ return entity.is_a("IfcSpace") or entity.is_a("IfcExternalSpatialElement")
+ return False
+
+
+def boundary_filter(self, object):
+ entity = tool.Ifc.get_entity(object)
+ if entity:
+ return entity.is_a("IfcRelSpaceBoundary")
+ return False
+
+
+def element_filter(self, object):
+ entity = tool.Ifc.get_entity(object)
+ if entity:
+ return entity.is_a("IfcElement")
+ return False
+
+
+class BIMBoundaryProperties(PropertyGroup):
+ is_editing: BoolProperty(name="Is Editing")
+ relating_space: PointerProperty(name="RelatingSpace", type=bpy.types.Object, poll=space_filter)
+ related_building_element: PointerProperty(name="RelatedBuildingElement", type=bpy.types.Object, poll=element_filter)
+ parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter)
+ corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter)
diff --git a/src/blenderbim/blenderbim/bim/module/boundary/ui.py b/src/blenderbim/blenderbim/bim/module/boundary/ui.py
index ab8cd0e6bd..56d4444ac2 100644
--- a/src/blenderbim/blenderbim/bim/module/boundary/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/boundary/ui.py
@@ -37,15 +37,80 @@ class BIM_PT_SceneBoundaries(Panel):
return IfcStore.get_file()
def draw(self, context):
- row = self.layout.row()
+ row = self.layout.row(align=True)
row.operator("bim.load_project_space_boundaries")
row.operator("bim.select_project_space_boundaries", text="", icon="RESTRICT_SELECT_OFF")
row.operator("bim.colour_by_related_building_element", text="", icon="BRUSH_DATA")
class BIM_PT_Boundary(Panel):
- bl_label = "IFC Space Boundaries"
+ bl_label = "IFC Space Boundary"
bl_idname = "BIM_PT_Boundary"
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "object"
+
+ @classmethod
+ def poll(cls, context):
+ if not context.active_object:
+ return False
+ props = context.active_object.BIMObjectProperties
+ if not props.ifc_definition_id:
+ return False
+ if not IfcStore.get_element(props.ifc_definition_id):
+ return False
+ entity = IfcStore.get_file().by_id(props.ifc_definition_id)
+ return entity.is_a("IfcRelSpaceBoundary")
+
+ def draw(self, context):
+ props = context.active_object.BIMObjectProperties
+ ifc_file = tool.Ifc.get()
+ element = ifc_file.by_id(props.ifc_definition_id)
+ boundary = ifc_file.by_id(props.ifc_definition_id)
+ self.bprops = context.active_object.bim_boundary_properties
+ if self.bprops.is_editing:
+ row = self.layout.row(align=True)
+ row.operator("bim.edit_boundary_attributes", icon="CHECKMARK", text="Save Attributes")
+ row.operator("bim.disable_editing_boundary", icon="CANCEL", text="")
+ self.draw_relation_editor(boundary, "RelatingSpace", "relating_space")
+ self.draw_relation_editor(boundary, "RelatedBuildingElement", "related_building_element")
+ self.draw_relation_editor(boundary, "ParentBoundary", "parent_boundary")
+ self.draw_relation_editor(boundary, "CorrespondingBoundary", "corresponding_boundary")
+ else:
+ row = self.layout.row()
+ row.operator("bim.enable_editing_boundary", icon="GREASEPENCIL", text="Edit")
+ self.draw_relation_data(boundary, "RelatingSpace")
+ self.draw_relation_data(boundary, "RelatedBuildingElement")
+ self.draw_relation_data(boundary, "ParentBoundary")
+ self.draw_relation_data(boundary, "CorrespondingBoundary")
+ if hasattr(boundary, "InnerBoundaries"):
+ for i, inner_boundary in enumerate(getattr(boundary, "InnerBoundaries", ())):
+ row = self.layout.row(align=True)
+ row.label(text="InnerBoundaries")
+ row.label(text=f"[{i}]")
+ row.label(text=f"{inner_boundary.is_a()}/{inner_boundary.Name}")
+
+ def draw_relation_data(self, boundary, ifc_attribute: str):
+ row = self.layout.row(align=True)
+ if hasattr(boundary, ifc_attribute):
+ row.label(text=ifc_attribute)
+ entity = getattr(boundary, ifc_attribute)
+ if entity:
+ row.label(text=f"{entity.is_a()}/{entity.Name}")
+ op = row.operator("bim.select_global_id", text="", icon="TRACKER")
+ op.global_id = entity.GlobalId
+ else:
+ row.label(text="")
+
+ def draw_relation_editor(self, boundary, ifc_attribute: str, blender_property: str):
+ if hasattr(boundary, ifc_attribute):
+ row = self.layout.row(align=True)
+ row.prop(self.bprops, blender_property)
+
+
+class BIM_PT_SpaceBoundaries(Panel):
+ bl_label = "IFC Space Boundaries"
+ bl_idname = "BIM_PT_SpaceBoundaries"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
@@ -60,20 +125,23 @@ class BIM_PT_Boundary(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
- if IfcStore.get_file().by_id(props.ifc_definition_id).is_a() not in ["IfcSpace", "IfcExternalSpatialElement"]:
- return False
- return True
+ valid_classes = ("IfcSpace", "IfcExternalSpatialElement")
+ entity = IfcStore.get_file().by_id(props.ifc_definition_id)
+ for ifc_class in valid_classes:
+ if entity.is_a(ifc_class):
+ return True
+ return False
def draw(self, context):
- self.oprops = context.active_object.BIMObjectProperties
+ self.props = context.active_object.BIMObjectProperties
+ self.ifc_file = tool.Ifc.get()
row = self.layout.row()
row.operator("bim.load_space_boundaries")
- ifc_file = tool.Ifc.get()
if not Data.is_loaded:
- Data.load(ifc_file)
- for boundary_id in Data.spaces.get(self.oprops.ifc_definition_id, []):
- boundary = Data.boundaries[boundary_id]
- building_element = ifc_file.by_id(boundary["RelatedBuildingElement"])
+ Data.load(self.ifc_file)
+ for boundary_id in Data.spaces.get(self.props.ifc_definition_id, []):
+ boundary_data = Data.boundaries[boundary_id]
+ building_element = self.ifc_file.by_id(boundary_data["RelatedBuildingElement"])
row = self.layout.row()
row.label(text=f"{boundary_id} > {building_element.is_a()}/{building_element.Name}", icon="GHOST_ENABLED")
op = row.operator("bim.load_boundary", text="", icon="RESTRICT_SELECT_OFF")
diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py
index 8b574accef..1b20ba07cb 100644
--- a/src/blenderbim/blenderbim/bim/module/search/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/search/operator.py
@@ -63,16 +63,13 @@ class SelectGlobalId(bpy.types.Operator):
global_id: bpy.props.StringProperty()
def execute(self, context):
- self.file = IfcStore.get_file()
+ ifc_file = tool.Ifc.get()
props = context.scene.BIMSearchProperties
global_id = self.global_id or props.global_id
- for obj in context.visible_objects:
- if not obj.BIMObjectProperties.ifc_definition_id:
- continue
- element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
- if element.GlobalId == global_id:
- obj.select_set(True)
- break
+ entity = ifc_file.by_guid(global_id)
+ obj = tool.Ifc.get_object(entity)
+ obj.select_set(True)
+ bpy.context.view_layer.objects.active = obj
return {"FINISHED"}