mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Implement support for managing nest hosts and components.
This commit is contained in:
@@ -44,6 +44,7 @@ modules = {
|
||||
"spatial": None,
|
||||
"void": None,
|
||||
"aggregate": None,
|
||||
"nest": None,
|
||||
"geometry": None,
|
||||
"fm": None,
|
||||
"resource": None,
|
||||
|
||||
@@ -20,14 +20,14 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
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_select_parts,
|
||||
operator.BIM_OT_select_aggregate,
|
||||
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,
|
||||
ui.BIM_PT_aggregate,
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ class AggregateData:
|
||||
"has_relating_object": cls.has_relating_object(),
|
||||
"relating_object_label": cls.get_relating_object_label(),
|
||||
"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(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
@@ -59,7 +59,7 @@ class AggregateData:
|
||||
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
|
||||
@classmethod
|
||||
def get_related_objects_amount(cls):
|
||||
def total_parts(cls):
|
||||
parts = cls.get_related_objects()
|
||||
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 = layout.row(align=True)
|
||||
parts = AggregateData.data["related_objects_amount"]
|
||||
row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN")
|
||||
total_parts = AggregateData.data["total_parts"]
|
||||
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"]:
|
||||
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||
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
|
||||
|
||||
|
||||
@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
|
||||
class Patch:
|
||||
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.misc import Misc
|
||||
from blenderbim.tool.model import Model
|
||||
from blenderbim.tool.nest import Nest
|
||||
from blenderbim.tool.owner import Owner
|
||||
from blenderbim.tool.patch import Patch
|
||||
from blenderbim.tool.project import Project
|
||||
|
||||
@@ -107,11 +107,12 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
collection.children.unlink(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
|
||||
def _get_own_collection(cls, element, obj):
|
||||
if obj.BIMObjectProperties.collection:
|
||||
return obj.BIMObjectProperties.collection
|
||||
|
||||
if element.is_a("IfcProject"):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
@@ -155,6 +156,9 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
if getattr(element, "IsDecomposedBy", None):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
if getattr(element, "IsNestedBy", None):
|
||||
return cls._create_own_collection(obj)
|
||||
|
||||
@classmethod
|
||||
def _get_collection(cls, element, obj):
|
||||
if element.is_a("IfcTypeObject"):
|
||||
@@ -235,6 +239,8 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
|
||||
@classmethod
|
||||
def _create_own_collection(cls, obj):
|
||||
if obj.BIMObjectProperties.collection:
|
||||
return obj.BIMObjectProperties.collection
|
||||
collection = bpy.data.collections.new(obj.name)
|
||||
obj.BIMObjectProperties.collection = collection
|
||||
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()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nest():
|
||||
prophet = Prophecy(blenderbim.core.tool.Nest)
|
||||
yield prophet
|
||||
prophet.verify()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TestCanAggregate(NewFile):
|
||||
def test_elements_can_aggregate(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -107,6 +103,7 @@ class TestCanAggregate(NewFile):
|
||||
tool.Ifc.link(subelement, subelement_obj)
|
||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||
|
||||
|
||||
class TestDisableEditing(NewFile):
|
||||
def test_run(self):
|
||||
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)
|
||||
"""
|
||||
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):
|
||||
@@ -821,13 +822,17 @@ def get_nest(element):
|
||||
element = file.by_type("IfcBeam")[0]
|
||||
aggregate = ifcopenshell.util.element.get_nest(element)
|
||||
"""
|
||||
if hasattr(element, "Nests") and element.Nests:
|
||||
return element.Nests[0].RelatingObject
|
||||
if hasattr(element, "Nests"):
|
||||
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):
|
||||
"""
|
||||
Retrieves the parts of an element.
|
||||
Retrieves the parts of an element that have an aggregation relationship.
|
||||
|
||||
:param element: The IFC element
|
||||
:return: The parts of the element
|
||||
@@ -840,7 +845,30 @@ def get_parts(element):
|
||||
|
||||
"""
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user