Implement support for managing nest hosts and components.

This commit is contained in:
Dion Moult
2023-12-07 13:18:33 +11:00
parent 302dc8eb9f
commit 401db55c55
19 changed files with 704 additions and 22 deletions
@@ -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
+58
View File
@@ -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)
+8
View File
@@ -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
+9 -3
View File
@@ -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
+49
View File
@@ -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)
+7
View File
@@ -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)
+59
View File
@@ -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")
+1 -4
View File
@@ -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)
+74
View File
@@ -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):