mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +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
|
layout = self.layout
|
||||||
props = context.active_object.data.BIMMeshProperties
|
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 = layout.row()
|
||||||
row.operator("bim.copy_representation")
|
row.operator("bim.copy_representation")
|
||||||
|
|
||||||
|
|||||||
@@ -22,22 +22,16 @@ def copy_class(ifc, collector, geometry, root, obj=None):
|
|||||||
if not element:
|
if not element:
|
||||||
return
|
return
|
||||||
representation = root.get_object_representation(obj)
|
representation = root.get_object_representation(obj)
|
||||||
element = ifc.run("root.copy_class", product=element)
|
new = ifc.run("root.copy_class", product=element)
|
||||||
ifc.link(element, obj)
|
ifc.link(new, obj)
|
||||||
relating_type = root.get_element_type(element)
|
relating_type = root.get_element_type(new)
|
||||||
if relating_type and root.does_type_have_representations(relating_type):
|
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)
|
root.link_object_data(ifc.get_object(relating_type), obj)
|
||||||
else:
|
elif representation:
|
||||||
if representation:
|
root.copy_representation(element, new)
|
||||||
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),
|
|
||||||
)
|
|
||||||
collector.assign(obj)
|
collector.assign(obj)
|
||||||
if root.is_opening_element(element):
|
if root.is_opening_element(new):
|
||||||
root.add_tracked_opening(obj)
|
root.add_tracked_opening(obj)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -444,6 +444,7 @@ class Resource:
|
|||||||
@interface
|
@interface
|
||||||
class Root:
|
class Root:
|
||||||
def add_tracked_opening(cls, obj): pass
|
def add_tracked_opening(cls, obj): pass
|
||||||
|
def copy_representation(cls, source, dest): pass
|
||||||
def does_type_have_representations(cls, element): pass
|
def does_type_have_representations(cls, element): pass
|
||||||
def get_element_type(cls, element): pass
|
def get_element_type(cls, element): pass
|
||||||
def get_object_name(cls, obj): 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 = bpy.context.scene.BIMModelProperties.openings.add()
|
||||||
new.obj = obj
|
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
|
@classmethod
|
||||||
def does_type_have_representations(cls, element):
|
def does_type_have_representations(cls, element):
|
||||||
return bool(element.RepresentationMaps)
|
return bool(element.RepresentationMaps)
|
||||||
|
|||||||
@@ -40,25 +40,17 @@ class TestCopyClass:
|
|||||||
root.is_opening_element("element").should_be_called().will_return(False)
|
root.is_opening_element("element").should_be_called().will_return(False)
|
||||||
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
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")
|
ifc.get_entity("obj").should_be_called().will_return("original_element")
|
||||||
root.get_object_representation("obj").should_be_called().will_return("representation")
|
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.run("root.copy_class", product="original_element").should_be_called().will_return("element")
|
||||||
ifc.link("element", "obj").should_be_called()
|
ifc.link("element", "obj").should_be_called()
|
||||||
root.get_element_type("element").should_be_called().will_return("type")
|
root.get_element_type("element").should_be_called().will_return("type")
|
||||||
root.does_type_have_representations("type").should_be_called().will_return(False)
|
root.does_type_have_representations("type").should_be_called().will_return(False)
|
||||||
|
root.copy_representation("original_element", "element").should_be_called()
|
||||||
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()
|
|
||||||
collector.assign("obj").should_be_called()
|
collector.assign("obj").should_be_called()
|
||||||
root.is_opening_element("element").should_be_called().will_return(False)
|
root.is_opening_element("element").should_be_called().will_return(False)
|
||||||
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
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)
|
root.is_opening_element("element").should_be_called().will_return(False)
|
||||||
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
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")
|
ifc.get_entity("obj").should_be_called().will_return("original_element")
|
||||||
root.get_object_representation("obj").should_be_called().will_return(None)
|
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.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
|
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):
|
class TestDoesTypeHaveRepresentations(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
|
|||||||
@@ -178,8 +178,7 @@ Create a simple model from scratch
|
|||||||
model = ifcopenshell.file()
|
model = ifcopenshell.file()
|
||||||
|
|
||||||
# All projects must have one IFC Project element
|
# All projects must have one IFC Project element
|
||||||
project = run("root.create_entity", model, ifc_class="IfcProject")
|
project = run("root.create_entity", model, ifc_class="IfcProject", name="My Project")
|
||||||
project.Name = "My Project"
|
|
||||||
|
|
||||||
# Geometry is optional in IFC, but because we want to use geometry in this example, let's define units
|
# 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
|
# 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")
|
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
|
# In particular, in this example we want to store the 3D "body" geometry of objects, i.e. the body shape
|
||||||
body = run(
|
body = run(
|
||||||
"context.add_context",
|
"context.add_context", model,
|
||||||
model,
|
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
|
||||||
context_type="Model",
|
|
||||||
context_identifier="Body",
|
|
||||||
target_view="MODEL_VIEW",
|
|
||||||
parent=context,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create a site, building, and storey. Many hierarchies are possible.
|
# Create a site, building, and storey. Many hierarchies are possible.
|
||||||
site = run("root.create_entity", model, ifc_class="IfcSite")
|
site = run("root.create_entity", model, ifc_class="IfcSite", name="My Site")
|
||||||
building = run("root.create_entity", model, ifc_class="IfcBuilding")
|
building = run("root.create_entity", model, ifc_class="IfcBuilding", name="Building A")
|
||||||
storey = run("root.create_entity", model, ifc_class="IfcBuildingStorey")
|
storey = run("root.create_entity", model, ifc_class="IfcBuildingStorey", name="Ground Floor")
|
||||||
|
|
||||||
# Let's name them to be neat.
|
|
||||||
site.Name = "My Site"
|
|
||||||
building.Name = "Building A"
|
|
||||||
storey.Name = "Ground Floor"
|
|
||||||
|
|
||||||
# Since the site is our top level location, assign it to the project
|
# 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
|
# 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
|
# For now, we do copy opening representations
|
||||||
if opening.Representation:
|
if opening.Representation:
|
||||||
new_opening.Representation = ifcopenshell.util.element.copy_deep(
|
new_opening.Representation = ifcopenshell.util.element.copy_deep(
|
||||||
self.file, opening.Representation
|
self.file, opening.Representation, exclude=["IfcGeometricRepresentationContext"]
|
||||||
)
|
)
|
||||||
elif inverse.is_a("IfcRelFillsElement"):
|
elif inverse.is_a("IfcRelFillsElement"):
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user