See #1676. You can now duplicate space boundaries.

This commit is contained in:
Dion Moult
2023-05-03 20:37:59 +10:00
parent 41e88636ff
commit c615f7469a
12 changed files with 137 additions and 15 deletions
@@ -491,8 +491,7 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
element = tool.Ifc.get_entity(obj)
for rel in element.BoundedBy or []:
boundary_obj = loader.load_boundary(rel, obj)
new = props.boundaries.add()
new.obj = boundary_obj
tool.Boundary.decorate_boundary(boundary_obj)
BoundaryDecorator.install(bpy.context)
return {"FINISHED"}
@@ -527,6 +527,8 @@ class OverrideDuplicateMove(bpy.types.Operator):
array_pset = tool.Ifc.get().by_id(array_pset["id"])
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=new, pset=array_pset)
old_to_new[tool.Ifc.get_entity(obj)] = [new]
if new.is_a("IfcRelSpaceBoundary"):
tool.Boundary.decorate_boundary(new_obj)
# Recreate decompositions
tool.Root.recreate_decompositions(relationships, old_to_new)
blenderbim.bim.handler.purge_module_data()
+5 -1
View File
@@ -21,6 +21,10 @@ def copy_class(ifc, collector, geometry, root, obj=None):
element = ifc.get_entity(obj)
if not element:
return
if root.is_element_a(element, "IfcRelSpaceBoundary"):
new = ifc.run("boundary.copy_boundary", boundary=element)
ifc.link(new, obj)
return new
representation = root.get_object_representation(obj)
new = ifc.run("root.copy_class", product=element)
ifc.link(new, obj)
@@ -38,7 +42,7 @@ def copy_class(ifc, collector, geometry, root, obj=None):
geometry.link(new_representation, data)
root.assign_body_styles(new, obj)
collector.assign(obj)
if root.is_opening_element(new):
if root.is_element_a(new, "IfcOpeningElement"):
root.add_tracked_opening(obj)
return new
+1 -1
View File
@@ -606,7 +606,7 @@ class Root:
def get_object_name(cls, obj): pass
def get_object_representation(cls, obj): pass
def get_representation_context(cls, representation): pass
def is_opening_element(cls, element): pass
def is_element_a(cls, element, ifc_class): pass
def link_object_data(cls, source_obj, destination_obj): pass
def recreate_decompositions(cls, relationships, old_to_new): pass
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
@@ -70,3 +70,9 @@ class Boundary(blenderbim.core.tool.Boundary):
translation = obj.matrix_world.translation - space.matrix_world.translation
obj.data.transform(mathutils.Matrix.Translation(translation))
obj.matrix_world = space.matrix_world
@classmethod
def decorate_boundary(cls, obj):
new = bpy.context.scene.BIMBoundaryProperties.boundaries.add()
new.obj = obj
obj.show_in_front = True
+2 -2
View File
@@ -119,8 +119,8 @@ class Root(blenderbim.core.tool.Root):
return representation.ContextOfItems
@classmethod
def is_opening_element(cls, element):
return element.is_a("IfcOpeningElement")
def is_element_a(cls, element, ifc_class):
return element.is_a(ifc_class)
@classmethod
def link_object_data(cls, source_obj, destination_obj):
+16 -5
View File
@@ -28,6 +28,7 @@ class TestCopyClass:
def test_copy_with_new_geometry_derived_from_the_type(self, ifc, collector, root):
ifc.get_entity("obj").should_be_called().will_return("original_element")
root.is_element_a("original_element", "IfcRelSpaceBoundary").should_be_called().will_return(False)
root.get_object_representation("obj").should_be_called().will_return("representation")
ifc.run("root.copy_class", product="original_element").should_be_called().will_return("element")
ifc.link("element", "obj").should_be_called()
@@ -37,7 +38,7 @@ class TestCopyClass:
ifc.get_object("type").should_be_called().will_return("type_obj")
root.link_object_data("type_obj", "obj").should_be_called()
collector.assign("obj").should_be_called()
root.is_opening_element("element").should_be_called().will_return(False)
root.is_element_a("element", "IfcOpeningElement").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copy_with_new_geometry_copied_from_the_old(self, ifc, collector, geometry, root):
@@ -45,6 +46,7 @@ class TestCopyClass:
# copied. This was faster (though I cannot recreate it now) but had the
# bigger problem of not preserving non-mesh geometry and openings.
ifc.get_entity("obj").should_be_called().will_return("original_element")
root.is_element_a("original_element", "IfcRelSpaceBoundary").should_be_called().will_return(False)
root.get_object_representation("obj").should_be_called().will_return("representation")
ifc.run("root.copy_class", product="original_element").should_be_called().will_return("element")
ifc.link("element", "obj").should_be_called()
@@ -60,22 +62,24 @@ class TestCopyClass:
root.assign_body_styles("element", "obj").should_be_called()
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
collector.assign("obj").should_be_called()
root.is_opening_element("element").should_be_called().will_return(False)
root.is_element_a("element", "IfcOpeningElement").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copy_with_no_new_geometry(self, ifc, collector, geometry, root):
ifc.get_entity("obj").should_be_called().will_return("original_element")
root.is_element_a("original_element", "IfcRelSpaceBoundary").should_be_called().will_return(False)
root.get_object_representation("obj").should_be_called().will_return(None)
ifc.run("root.copy_class", product="original_element").should_be_called().will_return("element")
ifc.link("element", "obj").should_be_called()
root.get_element_type("element").should_be_called().will_return("type")
root.does_type_have_representations("type").should_be_called().will_return(False)
collector.assign("obj").should_be_called()
root.is_opening_element("element").should_be_called().will_return(False)
root.is_element_a("element", "IfcOpeningElement").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copied_openings_are_tracked_for_special_visualiation(self, ifc, collector, root):
def test_copied_openings_are_tracked_for_special_visualiation(self, ifc, collector, geometry, root):
ifc.get_entity("obj").should_be_called().will_return("original_element")
root.is_element_a("original_element", "IfcRelSpaceBoundary").should_be_called().will_return(False)
root.get_object_representation("obj").should_be_called().will_return(None)
ifc.run("root.copy_class", product="original_element").should_be_called().will_return("element")
ifc.link("element", "obj").should_be_called()
@@ -85,10 +89,17 @@ class TestCopyClass:
ifc.get_object("type").should_be_called().will_return("type_obj")
root.link_object_data("type_obj", "obj").should_be_called()
collector.assign("obj").should_be_called()
root.is_opening_element("element").should_be_called().will_return(True)
root.is_element_a("element", "IfcOpeningElement").should_be_called().will_return(True)
root.add_tracked_opening("obj").should_be_called()
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copying_boundaries_are_dealt_with_specially(self, ifc, collector, geometry, root):
ifc.get_entity("obj").should_be_called().will_return("original_element")
root.is_element_a("original_element", "IfcRelSpaceBoundary").should_be_called().will_return(True)
ifc.run("boundary.copy_boundary", boundary="original_element").should_be_called().will_return("element")
ifc.link("element", "obj").should_be_called()
assert subject.copy_class(ifc, collector, geometry, root, obj="obj") == "element"
class TestAssignClass:
def test_do_nothing_if_already_assigned(self, ifc, collector, root):
+3 -3
View File
@@ -132,11 +132,11 @@ class TestGetRepresentationContext(NewFile):
assert subject.get_representation_context(representation) == context
class TestIsOpeningElement(NewFile):
class TestIsElementA(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
assert subject.is_opening_element(ifc.createIfcWall()) is False
assert subject.is_opening_element(ifc.createIfcOpeningElement()) is True
assert subject.is_a(ifc.createIfcWall(), "IfcSlab") is False
assert subject.is_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True
class TestLinkObjectData(NewFile):
@@ -0,0 +1,47 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, boundary=None):
"""Copies a space boundary
:param boundary: The IfcRelSpaceBoundary you want to copy.
:type boundary: ifcopenshell.entity_instance.entity_instance
:return: None
:rtype: None
Example:
# A boring boundary with no geometry. Note that this boundary is
# invalid and does not relate to any space or building element.
boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
# And now we have two
boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary)
"""
self.file = file
self.settings = {"boundary": boundary}
def execute(self):
result = ifcopenshell.util.element.copy(self.file, self.settings["boundary"])
if result.ConnectionGeometry:
result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(self.file, result.ConnectionGeometry)
return result
@@ -38,7 +38,7 @@ class Usecase:
boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
# Let's remove it!
ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary)
"""
self.file = file
self.settings = {"boundary": boundary}
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,36 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
class TestCopyBoundary(test.bootstrap.IFC4):
def test_run(self):
boundary = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelSpaceBoundary")
boundary2 = ifcopenshell.api.run("boundary.copy_boundary", self.file, boundary=boundary)
assert boundary2.is_a("IfcRelSpaceBoundary")
assert boundary2.GlobalId != boundary.GlobalId
def test_copying_connection_geometry(self):
geometry = self.file.createIfcConnectionSurfaceGeometry()
boundary = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelSpaceBoundary")
boundary.ConnectionGeometry = geometry
boundary2 = ifcopenshell.api.run("boundary.copy_boundary", self.file, boundary=boundary)
assert boundary2.ConnectionGeometry.is_a("IfcConnectionSurfaceGeometry")
assert boundary2.ConnectionGeometry != boundary.ConnectionGeometry