mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Fix #2488. Copying objects now copy deep instead of adding afresh (and losing parametric data and openings).
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user