mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Implement support for managing nest hosts and components.
This commit is contained in:
@@ -44,6 +44,7 @@ modules = {
|
|||||||
"spatial": None,
|
"spatial": None,
|
||||||
"void": None,
|
"void": None,
|
||||||
"aggregate": None,
|
"aggregate": None,
|
||||||
|
"nest": None,
|
||||||
"geometry": None,
|
"geometry": None,
|
||||||
"fm": None,
|
"fm": None,
|
||||||
"resource": None,
|
"resource": None,
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ import bpy
|
|||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.BIM_OT_assign_object,
|
|
||||||
operator.BIM_OT_unassign_object,
|
|
||||||
operator.BIM_OT_enable_editing_aggregate,
|
|
||||||
operator.BIM_OT_disable_editing_aggregate,
|
|
||||||
operator.BIM_OT_add_aggregate,
|
operator.BIM_OT_add_aggregate,
|
||||||
operator.BIM_OT_select_parts,
|
|
||||||
operator.BIM_OT_select_aggregate,
|
|
||||||
operator.BIM_OT_add_part_to_object,
|
operator.BIM_OT_add_part_to_object,
|
||||||
|
operator.BIM_OT_assign_object,
|
||||||
|
operator.BIM_OT_disable_editing_aggregate,
|
||||||
|
operator.BIM_OT_enable_editing_aggregate,
|
||||||
|
operator.BIM_OT_select_aggregate,
|
||||||
|
operator.BIM_OT_select_parts,
|
||||||
|
operator.BIM_OT_unassign_object,
|
||||||
prop.BIMObjectAggregateProperties,
|
prop.BIMObjectAggregateProperties,
|
||||||
ui.BIM_PT_aggregate,
|
ui.BIM_PT_aggregate,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class AggregateData:
|
|||||||
"has_relating_object": cls.has_relating_object(),
|
"has_relating_object": cls.has_relating_object(),
|
||||||
"relating_object_label": cls.get_relating_object_label(),
|
"relating_object_label": cls.get_relating_object_label(),
|
||||||
"has_related_objects": cls.has_related_objects(),
|
"has_related_objects": cls.has_related_objects(),
|
||||||
"related_objects_amount": cls.get_related_objects_amount(),
|
"total_parts": cls.total_parts(),
|
||||||
"ifc_class": cls.ifc_class(),
|
"ifc_class": cls.ifc_class(),
|
||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
@@ -59,7 +59,7 @@ class AggregateData:
|
|||||||
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
|
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_objects_amount(cls):
|
def total_parts(cls):
|
||||||
parts = cls.get_related_objects()
|
parts = cls.get_related_objects()
|
||||||
return len(parts) if parts else 0
|
return len(parts) if parts else 0
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,14 @@ class BIM_PT_aggregate(Panel):
|
|||||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
parts = AggregateData.data["related_objects_amount"]
|
total_parts = AggregateData.data["total_parts"]
|
||||||
row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN")
|
if total_parts == 0:
|
||||||
|
row.label(text="No Parts", icon="TRIA_DOWN")
|
||||||
|
elif total_parts == 1:
|
||||||
|
row.label(text="1 Part", icon="TRIA_DOWN")
|
||||||
|
else:
|
||||||
|
row.label(text=f"{total_parts} Parts", icon="TRIA_DOWN")
|
||||||
|
|
||||||
if AggregateData.data["has_related_objects"]:
|
if AggregateData.data["has_related_objects"]:
|
||||||
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
op.obj = context.active_object.name
|
op.obj = context.active_object.name
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.BIM_OT_assign_object,
|
||||||
|
operator.BIM_OT_disable_editing_nest,
|
||||||
|
operator.BIM_OT_enable_editing_nest,
|
||||||
|
operator.BIM_OT_select_components,
|
||||||
|
operator.BIM_OT_select_nest,
|
||||||
|
operator.BIM_OT_unassign_object,
|
||||||
|
prop.BIMObjectNestProperties,
|
||||||
|
ui.BIM_PT_nest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.Object.BIMObjectNestProperties = bpy.props.PointerProperty(type=prop.BIMObjectNestProperties)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Object.BIMObjectNestProperties
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
|
def refresh():
|
||||||
|
NestData.is_loaded = False
|
||||||
|
|
||||||
|
|
||||||
|
class NestData:
|
||||||
|
data = {}
|
||||||
|
is_loaded = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
cls.data = {
|
||||||
|
"has_relating_object": cls.has_relating_object(),
|
||||||
|
"relating_object_label": cls.get_relating_object_label(),
|
||||||
|
"has_related_objects": cls.has_related_objects(),
|
||||||
|
"total_components": cls.total_components(),
|
||||||
|
}
|
||||||
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_relating_object(cls):
|
||||||
|
return ifcopenshell.util.element.get_nest(tool.Ifc.get_entity(bpy.context.active_object))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_relating_object(cls) -> bool:
|
||||||
|
return cls.get_relating_object() is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_relating_object_label(cls) -> str:
|
||||||
|
nest = cls.get_relating_object()
|
||||||
|
if nest:
|
||||||
|
return f"{nest.is_a()}/{nest.Name or ''}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_related_objects(cls):
|
||||||
|
return ifcopenshell.util.element.get_components(tool.Ifc.get_entity(bpy.context.active_object))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def total_components(cls):
|
||||||
|
components = cls.get_related_objects()
|
||||||
|
return len(components) if components else 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_related_objects(cls) -> bool:
|
||||||
|
return bool(cls.get_related_objects())
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.nest as core
|
||||||
|
import blenderbim.core.spatial
|
||||||
|
import blenderbim.bim.handler
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class Operator:
|
||||||
|
def execute(self, context):
|
||||||
|
IfcStore.execute_ifc_operator(self, context)
|
||||||
|
blenderbim.bim.handler.refresh_ui_data()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_assign_object(bpy.types.Operator, Operator):
|
||||||
|
"""Create nest relationship between two ifc elements"""
|
||||||
|
|
||||||
|
bl_idname = "bim.assign_object"
|
||||||
|
bl_label = "Assign Object"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
relating_object: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
relating_obj = None
|
||||||
|
if self.relating_object:
|
||||||
|
relating_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object))
|
||||||
|
elif context.active_object:
|
||||||
|
relating_obj = context.active_object
|
||||||
|
if not relating_obj:
|
||||||
|
return
|
||||||
|
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if obj == relating_obj:
|
||||||
|
continue
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
result = core.assign_object(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Nest,
|
||||||
|
tool.Collector,
|
||||||
|
relating_obj=relating_obj,
|
||||||
|
related_obj=obj,
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
self.report({"ERROR"}, f" Cannot nest {obj.name} to {relating_obj.name}")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
|
||||||
|
"""Remove nest relationship between two ifc elements"""
|
||||||
|
|
||||||
|
bl_idname = "bim.unassign_object"
|
||||||
|
bl_label = "Unassign Object"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
nest = ifcopenshell.util.element.get_nest(element)
|
||||||
|
if not nest:
|
||||||
|
continue
|
||||||
|
core.unassign_object(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Nest,
|
||||||
|
tool.Collector,
|
||||||
|
relating_obj=tool.Ifc.get_object(nest),
|
||||||
|
related_obj=tool.Ifc.get_object(element),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_enable_editing_nest(bpy.types.Operator, Operator):
|
||||||
|
"""Enable editing nest relationship"""
|
||||||
|
|
||||||
|
bl_idname = "bim.enable_editing_nest"
|
||||||
|
bl_label = "Enable Editing Nest"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.enable_editing_nest(tool.Nest, obj=context.active_object)
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_disable_editing_nest(bpy.types.Operator, Operator):
|
||||||
|
"""Disable editing nest relationship"""
|
||||||
|
|
||||||
|
bl_idname = "bim.disable_editing_nest"
|
||||||
|
bl_label = "Disable Editing Nest"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.disable_editing_nest(tool.Nest, obj=context.active_object)
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_select_components(bpy.types.Operator):
|
||||||
|
"""Select Components"""
|
||||||
|
|
||||||
|
bl_idname = "bim.select_components"
|
||||||
|
bl_label = "Select Components"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
obj = bpy.data.objects.get(self.obj) or context.active_object
|
||||||
|
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(obj))
|
||||||
|
component_objs = set(tool.Ifc.get_object(c) for c in components)
|
||||||
|
selectable_component_objs = set(context.selectable_objects).intersection(component_objs)
|
||||||
|
for selectable_component_obj in selectable_component_objs:
|
||||||
|
selectable_component_obj.select_set(True)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_OT_select_nest(bpy.types.Operator):
|
||||||
|
"""Select Nest"""
|
||||||
|
|
||||||
|
bl_idname = "bim.select_nest"
|
||||||
|
bl_label = "Select Nest"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
obj = bpy.data.objects.get(self.obj) or context.active_object
|
||||||
|
nest = ifcopenshell.util.element.get_nest(tool.Ifc.get_entity(obj))
|
||||||
|
nest_obj = tool.Ifc.get_object(nest)
|
||||||
|
if nest_obj in context.selectable_objects:
|
||||||
|
nest_obj.select_set(True)
|
||||||
|
bpy.context.view_layer.objects.active = nest_obj
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
|
from blenderbim.bim.module.spatial.data import SpatialData
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_relating_object(self, context):
|
||||||
|
def message(self, context):
|
||||||
|
self.layout.label(text="Please select a valid Ifc Element")
|
||||||
|
|
||||||
|
if self.relating_object is None:
|
||||||
|
return
|
||||||
|
if not self.relating_object.BIMObjectProperties.ifc_definition_id:
|
||||||
|
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
|
||||||
|
self.relating_object = None
|
||||||
|
|
||||||
|
|
||||||
|
class BIMObjectNestProperties(PropertyGroup):
|
||||||
|
is_editing: BoolProperty(name="Is Editing")
|
||||||
|
relating_object: PointerProperty(name="Nest Host", type=bpy.types.Object, update=update_relating_object)
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from bpy.types import Panel
|
||||||
|
from blenderbim.bim.module.nest.data import NestData
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_nest(Panel):
|
||||||
|
bl_label = "Nest"
|
||||||
|
bl_idname = "BIM_PT_nest"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "object"
|
||||||
|
bl_parent_id = "BIM_PT_tab_object_metadata"
|
||||||
|
|
||||||
|
@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
|
||||||
|
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
if not NestData.is_loaded:
|
||||||
|
NestData.load()
|
||||||
|
|
||||||
|
props = context.active_object.BIMObjectNestProperties
|
||||||
|
|
||||||
|
if props.is_editing:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(props, "relating_object", text="")
|
||||||
|
if props.relating_object:
|
||||||
|
op = row.operator("bim.assign_object", icon="CHECKMARK", text="")
|
||||||
|
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
|
||||||
|
row.operator("bim.disable_editing_nest", icon="CANCEL", text="")
|
||||||
|
else:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
if NestData.data["has_relating_object"]:
|
||||||
|
row.label(text=NestData.data["relating_object_label"], icon="TRIA_UP")
|
||||||
|
op = row.operator("bim.select_nest", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
|
op.obj = context.active_object.name
|
||||||
|
row.operator("bim.enable_editing_nest", icon="GREASEPENCIL", text="")
|
||||||
|
op = row.operator("bim.unassign_object", icon="X", text="")
|
||||||
|
else:
|
||||||
|
row.label(text="No Host", icon="TRIA_UP")
|
||||||
|
row.operator("bim.enable_editing_nest", icon="GREASEPENCIL", text="")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
total_components = NestData.data["total_components"]
|
||||||
|
if total_components == 0:
|
||||||
|
row.label(text="No Components", icon="TRIA_DOWN")
|
||||||
|
elif total_components == 1:
|
||||||
|
row.label(text="1 Component", icon="TRIA_DOWN")
|
||||||
|
else:
|
||||||
|
row.label(text=f"{total_components} Components", icon="TRIA_DOWN")
|
||||||
|
|
||||||
|
if NestData.data["has_related_objects"]:
|
||||||
|
op = row.operator("bim.select_components", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
|
op.obj = context.active_object.name
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
def enable_editing_nest(nest, obj=None):
|
||||||
|
nest.enable_editing(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def disable_editing_nest(nest, obj=None):
|
||||||
|
nest.disable_editing(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def assign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
|
||||||
|
if not nest.can_nest(relating_obj, related_obj):
|
||||||
|
return
|
||||||
|
rel = ifc.run(
|
||||||
|
"nest.assign_object", related_object=ifc.get_entity(related_obj), relating_object=ifc.get_entity(relating_obj)
|
||||||
|
)
|
||||||
|
collector.assign(relating_obj)
|
||||||
|
collector.assign(related_obj)
|
||||||
|
nest.disable_editing(related_obj)
|
||||||
|
return rel
|
||||||
|
|
||||||
|
|
||||||
|
def unassign_object(ifc, nest, collector, relating_obj=None, related_obj=None):
|
||||||
|
related_element = ifc.get_entity(related_obj)
|
||||||
|
container = nest.get_container(related_element)
|
||||||
|
if not relating_obj:
|
||||||
|
relating_element = nest.get_relating_object(related_element)
|
||||||
|
if related_element:
|
||||||
|
relating_obj = ifc.get_object(relating_element)
|
||||||
|
if relating_obj:
|
||||||
|
ifc.run("nest.unassign_object", related_object=related_element)
|
||||||
|
if container:
|
||||||
|
ifc.run("spatial.assign_container", product=related_element, relating_structure=container)
|
||||||
|
collector.assign(relating_obj)
|
||||||
|
collector.assign(related_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def add_part_to_object(ifc, nest, collector, blender, obj, part_class, part_name=None):
|
||||||
|
part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name)
|
||||||
|
assign_object(ifc, nest, collector, relating_obj=obj, related_obj=part_obj)
|
||||||
|
blender.set_active_object(obj)
|
||||||
@@ -507,6 +507,14 @@ class Model:
|
|||||||
def replace_object_ifc_representation(cls, ifc_file, ifc_context, obj, new_representation): pass
|
def replace_object_ifc_representation(cls, ifc_file, ifc_context, obj, new_representation): pass
|
||||||
|
|
||||||
|
|
||||||
|
@interface
|
||||||
|
class Nest:
|
||||||
|
def can_nest(cls, relating_object, related_object): pass
|
||||||
|
def disable_editing(cls, obj): pass
|
||||||
|
def enable_editing(cls, obj): pass
|
||||||
|
def get_container(cls, element): pass
|
||||||
|
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Patch:
|
class Patch:
|
||||||
def run_migrate_patch(cls, infile, outfile, schema): pass
|
def run_migrate_patch(cls, infile, outfile, schema): pass
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from blenderbim.tool.loader import Loader
|
|||||||
from blenderbim.tool.material import Material
|
from blenderbim.tool.material import Material
|
||||||
from blenderbim.tool.misc import Misc
|
from blenderbim.tool.misc import Misc
|
||||||
from blenderbim.tool.model import Model
|
from blenderbim.tool.model import Model
|
||||||
|
from blenderbim.tool.nest import Nest
|
||||||
from blenderbim.tool.owner import Owner
|
from blenderbim.tool.owner import Owner
|
||||||
from blenderbim.tool.patch import Patch
|
from blenderbim.tool.patch import Patch
|
||||||
from blenderbim.tool.project import Project
|
from blenderbim.tool.project import Project
|
||||||
|
|||||||
@@ -107,11 +107,12 @@ class Collector(blenderbim.core.tool.Collector):
|
|||||||
collection.children.unlink(object_collection)
|
collection.children.unlink(object_collection)
|
||||||
collection_collection.children.link(object_collection)
|
collection_collection.children.link(object_collection)
|
||||||
|
|
||||||
|
# If an aggregate or nested host loses all its children, it no longer needs its own collection
|
||||||
|
if obj.BIMObjectProperties.collection and obj.BIMObjectProperties.collection != object_collection:
|
||||||
|
bpy.data.collections.remove(obj.BIMObjectProperties.collection)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_own_collection(cls, element, obj):
|
def _get_own_collection(cls, element, obj):
|
||||||
if obj.BIMObjectProperties.collection:
|
|
||||||
return obj.BIMObjectProperties.collection
|
|
||||||
|
|
||||||
if element.is_a("IfcProject"):
|
if element.is_a("IfcProject"):
|
||||||
return cls._create_own_collection(obj)
|
return cls._create_own_collection(obj)
|
||||||
|
|
||||||
@@ -155,6 +156,9 @@ class Collector(blenderbim.core.tool.Collector):
|
|||||||
if getattr(element, "IsDecomposedBy", None):
|
if getattr(element, "IsDecomposedBy", None):
|
||||||
return cls._create_own_collection(obj)
|
return cls._create_own_collection(obj)
|
||||||
|
|
||||||
|
if getattr(element, "IsNestedBy", None):
|
||||||
|
return cls._create_own_collection(obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_collection(cls, element, obj):
|
def _get_collection(cls, element, obj):
|
||||||
if element.is_a("IfcTypeObject"):
|
if element.is_a("IfcTypeObject"):
|
||||||
@@ -235,6 +239,8 @@ class Collector(blenderbim.core.tool.Collector):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_own_collection(cls, obj):
|
def _create_own_collection(cls, obj):
|
||||||
|
if obj.BIMObjectProperties.collection:
|
||||||
|
return obj.BIMObjectProperties.collection
|
||||||
collection = bpy.data.collections.new(obj.name)
|
collection = bpy.data.collections.new(obj.name)
|
||||||
obj.BIMObjectProperties.collection = collection
|
obj.BIMObjectProperties.collection = collection
|
||||||
collection.BIMCollectionProperties.obj = obj
|
collection.BIMCollectionProperties.obj = obj
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
|
class Nest(blenderbim.core.tool.Nest):
|
||||||
|
@classmethod
|
||||||
|
def can_nest(cls, relating_obj, related_obj):
|
||||||
|
relating_object = tool.Ifc.get_entity(relating_obj)
|
||||||
|
related_object = tool.Ifc.get_entity(related_obj)
|
||||||
|
if not relating_object or not related_object:
|
||||||
|
return False
|
||||||
|
if relating_object.is_a("IfcElement") and related_object.is_a("IfcElement"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def disable_editing(cls, obj):
|
||||||
|
obj.BIMObjectNestProperties.is_editing = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def enable_editing(cls, obj):
|
||||||
|
obj.BIMObjectNestProperties.is_editing = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_container(cls, element):
|
||||||
|
return ifcopenshell.util.element.get_container(element)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_relating_object(cls, related_element):
|
||||||
|
return ifcopenshell.util.element.get_nest(related_element)
|
||||||
@@ -126,6 +126,13 @@ def misc():
|
|||||||
prophet.verify()
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def nest():
|
||||||
|
prophet = Prophecy(blenderbim.core.tool.Nest)
|
||||||
|
yield prophet
|
||||||
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def owner():
|
def owner():
|
||||||
prophet = Prophecy(blenderbim.core.tool.Owner)
|
prophet = Prophecy(blenderbim.core.tool.Owner)
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import blenderbim.core.nest as subject
|
||||||
|
from test.core.bootstrap import ifc, nest, collector
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnableEditingNeset:
|
||||||
|
def test_run(self, nest):
|
||||||
|
nest.enable_editing("obj").should_be_called()
|
||||||
|
subject.enable_editing_nest(nest, obj="obj")
|
||||||
|
|
||||||
|
|
||||||
|
class TestDisableEditingNest:
|
||||||
|
def test_run(self, nest):
|
||||||
|
nest.disable_editing("obj").should_be_called()
|
||||||
|
subject.disable_editing_nest(nest, obj="obj")
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssignObject:
|
||||||
|
def test_run(self, ifc, nest, collector):
|
||||||
|
nest.can_nest("relating_obj", "related_obj").should_be_called().will_return(True)
|
||||||
|
ifc.get_entity("relating_obj").should_be_called().will_return("relating_object")
|
||||||
|
ifc.get_entity("related_obj").should_be_called().will_return("related_object")
|
||||||
|
ifc.run(
|
||||||
|
"nest.assign_object", related_object="related_object", relating_object="relating_object"
|
||||||
|
).should_be_called().will_return("rel")
|
||||||
|
nest.disable_editing("related_obj").should_be_called()
|
||||||
|
collector.assign("relating_obj").should_be_called()
|
||||||
|
collector.assign("related_obj").should_be_called()
|
||||||
|
assert (
|
||||||
|
subject.assign_object(ifc, nest, collector, relating_obj="relating_obj", related_obj="related_obj") == "rel"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignObject:
|
||||||
|
def test_run(self, ifc, nest, collector):
|
||||||
|
ifc.get_entity("related_obj").should_be_called().will_return("element")
|
||||||
|
nest.get_container("element").should_be_called().will_return("container")
|
||||||
|
ifc.run("spatial.assign_container", product="element", relating_structure="container").should_be_called()
|
||||||
|
ifc.run("nest.unassign_object", related_object="element").should_be_called().will_return("rel")
|
||||||
|
collector.assign("relating_obj").should_be_called()
|
||||||
|
collector.assign("related_obj").should_be_called()
|
||||||
|
subject.unassign_object(ifc, nest, collector, relating_obj="relating_obj", related_obj="related_obj")
|
||||||
@@ -29,10 +29,6 @@ class TestImplementsTool(NewFile):
|
|||||||
assert isinstance(subject(), blenderbim.core.tool.Aggregate)
|
assert isinstance(subject(), blenderbim.core.tool.Aggregate)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestCanAggregate(NewFile):
|
class TestCanAggregate(NewFile):
|
||||||
def test_elements_can_aggregate(self):
|
def test_elements_can_aggregate(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
@@ -107,6 +103,7 @@ class TestCanAggregate(NewFile):
|
|||||||
tool.Ifc.link(subelement, subelement_obj)
|
tool.Ifc.link(subelement, subelement_obj)
|
||||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||||
|
|
||||||
|
|
||||||
class TestDisableEditing(NewFile):
|
class TestDisableEditing(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
obj = bpy.data.objects.new("Object", None)
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from test.bim.bootstrap import NewFile
|
||||||
|
from blenderbim.tool.nest import Nest as subject
|
||||||
|
|
||||||
|
|
||||||
|
class TestImplementsTool(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
assert isinstance(subject(), blenderbim.core.tool.Nest)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCanNest(NewFile):
|
||||||
|
def test_elements_can_nest(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
element = ifc.createIfcElementAssembly()
|
||||||
|
element_obj = bpy.data.objects.new("Object", None)
|
||||||
|
tool.Ifc.link(element, element_obj)
|
||||||
|
subelement = ifc.createIfcBeam()
|
||||||
|
subelement_obj = bpy.data.objects.new("Object", None)
|
||||||
|
tool.Ifc.link(subelement, subelement_obj)
|
||||||
|
assert subject.can_nest(element_obj, subelement_obj) is True
|
||||||
|
|
||||||
|
def test_unlinked_elements_cannot_nest(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
element_obj = bpy.data.objects.new("Object", None)
|
||||||
|
subelement_obj = bpy.data.objects.new("Object", None)
|
||||||
|
assert subject.can_nest(element_obj, subelement_obj) is False
|
||||||
|
|
||||||
|
|
||||||
|
class TestDisableEditing(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
subject.enable_editing(obj)
|
||||||
|
subject.disable_editing(obj)
|
||||||
|
assert obj.BIMObjectNestProperties.is_editing is False
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnableEditing(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
subject.enable_editing(obj)
|
||||||
|
assert obj.BIMObjectNestProperties.is_editing is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetContainer(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
container = ifc.createIfcBuildingStorey()
|
||||||
|
ifcopenshell.api.run("spatial.assign_container", ifc, product=element, relating_structure=container)
|
||||||
|
assert subject.get_container(element) == container
|
||||||
@@ -805,7 +805,8 @@ def get_aggregate(element):
|
|||||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||||
"""
|
"""
|
||||||
if hasattr(element, "Decomposes") and element.Decomposes:
|
if hasattr(element, "Decomposes") and element.Decomposes:
|
||||||
return element.Decomposes[0].RelatingObject
|
if element.Decomposes[0].is_a("IfcRelAggregates"): # IFC2X3
|
||||||
|
return element.Decomposes[0].RelatingObject
|
||||||
|
|
||||||
|
|
||||||
def get_nest(element):
|
def get_nest(element):
|
||||||
@@ -821,13 +822,17 @@ def get_nest(element):
|
|||||||
element = file.by_type("IfcBeam")[0]
|
element = file.by_type("IfcBeam")[0]
|
||||||
aggregate = ifcopenshell.util.element.get_nest(element)
|
aggregate = ifcopenshell.util.element.get_nest(element)
|
||||||
"""
|
"""
|
||||||
if hasattr(element, "Nests") and element.Nests:
|
if hasattr(element, "Nests"):
|
||||||
return element.Nests[0].RelatingObject
|
if element.Nests:
|
||||||
|
return element.Nests[0].RelatingObject
|
||||||
|
elif hasattr(element, "Decomposes") and element.Decomposes: # IFC2X3
|
||||||
|
if element.Decomposes[0].is_a("IfcRelNests"):
|
||||||
|
return element.Decomposes[0].RelatingObject
|
||||||
|
|
||||||
|
|
||||||
def get_parts(element):
|
def get_parts(element):
|
||||||
"""
|
"""
|
||||||
Retrieves the parts of an element.
|
Retrieves the parts of an element that have an aggregation relationship.
|
||||||
|
|
||||||
:param element: The IFC element
|
:param element: The IFC element
|
||||||
:return: The parts of the element
|
:return: The parts of the element
|
||||||
@@ -840,7 +845,30 @@ def get_parts(element):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
if hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy:
|
if hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy:
|
||||||
return element.IsDecomposedBy[0].RelatedObjects
|
if element.IsDecomposedBy[0].is_a("IfcRelAggregates"):
|
||||||
|
return element.IsDecomposedBy[0].RelatedObjects
|
||||||
|
|
||||||
|
|
||||||
|
def get_components(element):
|
||||||
|
"""
|
||||||
|
Retrieves the components of an element that have an nest relationship.
|
||||||
|
|
||||||
|
:param element: The IFC element
|
||||||
|
:return: The components of the element
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
element = file.by_type("IfcElementAssembly")[0]
|
||||||
|
components = ifcopenshell.util.element.get_components(element)
|
||||||
|
|
||||||
|
"""
|
||||||
|
if hasattr(element, "IsNestedBy"):
|
||||||
|
if element.IsNestedBy:
|
||||||
|
return element.IsNestedBy[0].RelatedObjects
|
||||||
|
elif hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy:
|
||||||
|
if element.IsDecomposedBy[0].is_a("IfcRelNests"):
|
||||||
|
return element.IsDecomposedBy[0].RelatedObjects
|
||||||
|
|
||||||
|
|
||||||
def replace_attribute(element, old, new):
|
def replace_attribute(element, old, new):
|
||||||
|
|||||||
Reference in New Issue
Block a user