Fix #2488. Copying objects now copy deep instead of adding afresh (and losing parametric data and openings).

This commit is contained in:
Dion Moult
2022-10-09 18:09:22 +11:00
parent 232252f61d
commit bdbd874fa8
8 changed files with 55 additions and 56 deletions
@@ -127,18 +127,6 @@ class BIM_PT_mesh(Panel):
layout = self.layout
props = context.active_object.data.BIMMeshProperties
row = layout.row(align=True)
op = row.operator("bim.switch_representation", text="Bake Voids", icon="SELECT_SUBTRACT")
op.should_switch_all_meshes = True
op.should_reload = True
op.ifc_definition_id = props.ifc_definition_id
op.disable_opening_subtractions = False
op = row.operator("bim.switch_representation", text="Dynamic Voids", icon="SELECT_INTERSECT")
op.should_switch_all_meshes = True
op.should_reload = True
op.ifc_definition_id = props.ifc_definition_id
op.disable_opening_subtractions = True
row = layout.row()
row.operator("bim.copy_representation")
+7 -13
View File
@@ -22,22 +22,16 @@ def copy_class(ifc, collector, geometry, root, obj=None):
if not element:
return
representation = root.get_object_representation(obj)
element = ifc.run("root.copy_class", product=element)
ifc.link(element, obj)
relating_type = root.get_element_type(element)
new = ifc.run("root.copy_class", product=element)
ifc.link(new, obj)
relating_type = root.get_element_type(new)
if relating_type and root.does_type_have_representations(relating_type):
ifc.run("type.map_type_representations", related_object=element, relating_type=relating_type)
ifc.run("type.map_type_representations", related_object=new, relating_type=relating_type)
root.link_object_data(ifc.get_object(relating_type), obj)
else:
if representation:
root.run_geometry_add_representation(
obj=obj,
context=root.get_representation_context(representation),
ifc_representation_class=geometry.get_ifc_representation_class(element, representation),
profile_set_usage=geometry.get_profile_set_usage(element),
)
elif representation:
root.copy_representation(element, new)
collector.assign(obj)
if root.is_opening_element(element):
if root.is_opening_element(new):
root.add_tracked_opening(obj)
+1
View File
@@ -444,6 +444,7 @@ class Resource:
@interface
class Root:
def add_tracked_opening(cls, obj): pass
def copy_representation(cls, source, dest): pass
def does_type_have_representations(cls, element): pass
def get_element_type(cls, element): pass
def get_object_name(cls, obj): pass
+16
View File
@@ -30,6 +30,22 @@ class Root(blenderbim.core.tool.Root):
new = bpy.context.scene.BIMModelProperties.openings.add()
new.obj = obj
@classmethod
def copy_representation(cls, source, dest):
if dest.is_a("IfcProduct"):
if not source.Representation:
return
dest.Representation = ifcopenshell.util.element.copy_deep(
tool.Ifc.get(), source.Representation, exclude=["IfcGeometricRepresentationContext"]
)
elif dest.is_a("IfcTypeProduct"):
if not source.RepresentationMaps:
return
dest.RepresentationMaps = [
ifcopenshell.util.element.copy_deep(tool.Ifc.get(), m, exclude=["IfcGeometricRepresentationContext"])
for m in source.RepresentationMaps
]
@classmethod
def does_type_have_representations(cls, element):
return bool(element.RepresentationMaps)
+6 -14
View File
@@ -40,25 +40,17 @@ class TestCopyClass:
root.is_opening_element("element").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copy_with_new_geometry_added_afresh_for_speed(self, ifc, collector, geometry, root):
def test_copy_with_new_geometry_copied_from_the_old(self, ifc, collector, geometry, root):
# Originally, geometry was added fresh from the Blender mesh instead of
# 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.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()
root.get_element_type("element").should_be_called().will_return("type")
root.does_type_have_representations("type").should_be_called().will_return(False)
root.get_representation_context("representation").should_be_called().will_return("context")
geometry.get_ifc_representation_class("element", "representation").should_be_called().will_return(
"ifc_representation_class"
)
geometry.get_profile_set_usage("element").should_be_called().will_return("profile_set_usage")
root.run_geometry_add_representation(
obj="obj",
context="context",
ifc_representation_class="ifc_representation_class",
profile_set_usage="profile_set_usage",
).should_be_called()
root.copy_representation("original_element", "element").should_be_called()
collector.assign("obj").should_be_called()
root.is_opening_element("element").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
@@ -74,7 +66,7 @@ class TestCopyClass:
root.is_opening_element("element").should_be_called().will_return(False)
subject.copy_class(ifc, collector, geometry, root, obj="obj")
def test_copied_openings_have_dynamic_voids_added(self, ifc, collector, root):
def test_copied_openings_are_tracked_for_special_visualiation(self, ifc, collector, root):
ifc.get_entity("obj").should_be_called().will_return("original_element")
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")
+18
View File
@@ -37,6 +37,24 @@ class TestAddTrackedOpening(NewFile):
assert props.openings[0].obj == obj
class TestCopyRepresentation(NewFile):
def test_copying_a_product(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
source = ifc.createIfcWall(Representation=ifc.createIfcProductDefinitionShape())
dest = ifc.createIfcWall()
subject.copy_representation(source, dest)
assert dest.Representation.is_a("IfcProductDefinitionShape")
def test_copying_a_type_product(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
source = ifc.createIfcWallType(RepresentationMaps=[ifc.createIfcRepresentationMap()])
dest = ifc.createIfcWallType()
subject.copy_representation(source, dest)
assert dest.RepresentationMaps[0].is_a("IfcRepresentationMap")
class TestDoesTypeHaveRepresentations(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
@@ -178,8 +178,7 @@ Create a simple model from scratch
model = ifcopenshell.file()
# All projects must have one IFC Project element
project = run("root.create_entity", model, ifc_class="IfcProject")
project.Name = "My Project"
project = run("root.create_entity", model, ifc_class="IfcProject", name="My Project")
# Geometry is optional in IFC, but because we want to use geometry in this example, let's define units
# Assigning without arguments defaults to metric units
@@ -189,23 +188,14 @@ Create a simple model from scratch
context = run("context.add_context", model, context_type="Model")
# In particular, in this example we want to store the 3D "body" geometry of objects, i.e. the body shape
body = run(
"context.add_context",
model,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=context,
"context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
)
# Create a site, building, and storey. Many hierarchies are possible.
site = run("root.create_entity", model, ifc_class="IfcSite")
building = run("root.create_entity", model, ifc_class="IfcBuilding")
storey = run("root.create_entity", model, ifc_class="IfcBuildingStorey")
# Let's name them to be neat.
site.Name = "My Site"
building.Name = "Building A"
storey.Name = "Ground Floor"
site = run("root.create_entity", model, ifc_class="IfcSite", name="My Site")
building = run("root.create_entity", model, ifc_class="IfcBuilding", name="Building A")
storey = run("root.create_entity", model, ifc_class="IfcBuildingStorey", name="Ground Floor")
# Since the site is our top level location, assign it to the project
# Then place our building on the site, and our storey in the building
@@ -80,7 +80,7 @@ class Usecase:
# For now, we do copy opening representations
if opening.Representation:
new_opening.Representation = ifcopenshell.util.element.copy_deep(
self.file, opening.Representation
self.file, opening.Representation, exclude=["IfcGeometricRepresentationContext"]
)
elif inverse.is_a("IfcRelFillsElement"):
continue