mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Fix #5949. When duplicating objects, copy over IFC links (shape representation ID, item_ids, edge_item_ids) in a more reliable way
This commit is contained in:
@@ -27,7 +27,7 @@ if TYPE_CHECKING:
|
||||
|
||||
def copy_class(
|
||||
ifc: tool.Ifc, collector: tool.Collector, geometry: tool.Geometry, root: tool.Root, obj: bpy.types.Object
|
||||
) -> ifcopenshell.entity_instance:
|
||||
) -> ifcopenshell.entity_instance | None:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -43,14 +43,12 @@ def copy_class(
|
||||
ifc.run("type.map_type_representations", related_object=new, relating_type=relating_type)
|
||||
root.link_object_data(ifc.get_object(relating_type), obj)
|
||||
elif representation:
|
||||
root.copy_representation(element, new)
|
||||
new_representation = root.get_element_representation(new, root.get_representation_context(representation))
|
||||
copied_entities = root.copy_representation(element, new)
|
||||
data = geometry.duplicate_object_data(obj)
|
||||
if data:
|
||||
geometry.copy_data_links(data, copied_entities)
|
||||
geometry.change_object_data(obj, data, is_global=True)
|
||||
geometry.rename_object(data, geometry.get_representation_name(new_representation))
|
||||
geometry.link(new_representation, data)
|
||||
geometry.reload_representation_item_ids(new_representation, data)
|
||||
geometry.rename_object(data, geometry.get_representation_name(ifc.get_entity(data)))
|
||||
root.assign_body_styles(new, obj)
|
||||
collector.assign(obj)
|
||||
if root.is_element_a(new, "IfcOpeningElement"):
|
||||
|
||||
@@ -387,6 +387,7 @@ class Geometry:
|
||||
def clear_cache(cls, element): pass
|
||||
def clear_modifiers(cls, obj): pass
|
||||
def clear_scale(cls, obj): pass
|
||||
def copy_data_links(cls, data, copied_entities) -> None: pass
|
||||
def delete_data(cls, data): pass
|
||||
def delete_ifc_object(cls, obj): pass
|
||||
def delete_opening_object_placement(cls, opening): pass
|
||||
@@ -420,7 +421,6 @@ class Geometry:
|
||||
def record_object_position(cls, obj): pass
|
||||
def recreate_object_with_data(cls, obj, data): pass
|
||||
def reimport_element_representations(cls, obj, representation, apply_openings=True): pass
|
||||
def reload_representation_item_ids(cls, representation, data) -> None: pass
|
||||
def remove_connection(cls, connection): pass
|
||||
def rename_object(cls, obj, name): pass
|
||||
def replace_object_data_globally(cls, old_data, new_data): pass
|
||||
|
||||
@@ -259,10 +259,14 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
# a cylinder) so dissolving edges should not be allowed.
|
||||
mesh_element = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if (
|
||||
mesh_element.is_a("IfcShapeRepresentation")
|
||||
and ifcopenshell.util.representation.resolve_representation(mesh_element).RepresentationType
|
||||
== "AdvancedBrep"
|
||||
) or mesh_element.is_a("IfcAdvancedBrep") or not obj.data:
|
||||
(
|
||||
mesh_element.is_a("IfcShapeRepresentation")
|
||||
and ifcopenshell.util.representation.resolve_representation(mesh_element).RepresentationType
|
||||
== "AdvancedBrep"
|
||||
)
|
||||
or mesh_element.is_a("IfcAdvancedBrep")
|
||||
or not obj.data
|
||||
):
|
||||
return
|
||||
if hasattr(obj.data, "attributes") and (ios_edges_attribute := obj.data.attributes.get("ios_edges")):
|
||||
# Edges from a forced triangulation are stored as True in a boolean attribute on the mesh
|
||||
@@ -612,7 +616,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def get_representation_name(cls, representation: ifcopenshell.entity_instance) -> str:
|
||||
return tool.Loader.get_mesh_name(representation.ContextOfItems.id(), representation.id())
|
||||
return tool.Loader.get_mesh_name(representation)
|
||||
|
||||
@classmethod
|
||||
def get_styles(
|
||||
@@ -719,7 +723,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if not tool.Loader.is_native_swept_disk_solid(element, representation):
|
||||
continue
|
||||
if curve is None:
|
||||
mesh_name = tool.Loader.get_mesh_name(context.id(), representation.id())
|
||||
mesh_name = tool.Loader.get_mesh_name(representation)
|
||||
native_data = {
|
||||
"representation": representation,
|
||||
# TODO: calculate mapped item matrix.
|
||||
@@ -1729,8 +1733,14 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def reload_representation_item_ids(cls, representation: ifcopenshell.entity_instance, data: bpy.types.Mesh) -> None:
|
||||
data["ios_item_ids"] = [i["item"].id() for i in ifcopenshell.util.representation.resolve_items(representation)]
|
||||
def copy_data_links(cls, data: bpy.types.Mesh, copied_entities: dict[int, ifcopenshell.entity_instance]) -> None:
|
||||
representation = tool.Ifc.get_entity(data)
|
||||
representation = copied_entities.get(representation.id(), representation)
|
||||
tool.Ifc.link(representation, data)
|
||||
if item_ids := data.get("ios_item_ids"):
|
||||
data["ios_item_ids"] = [copied_entities.get(i, tool.Ifc.get().by_id(i)).id() for i in item_ids]
|
||||
if item_ids := data.get("ios_edges_item_ids"):
|
||||
data["ios_edges_item_ids"] = [copied_entities.get(i, tool.Ifc.get().by_id(i)).id() for i in item_ids]
|
||||
|
||||
@classmethod
|
||||
def export_mesh_to_tessellation(
|
||||
|
||||
@@ -102,7 +102,7 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
Return None if object is not linked to IFC or it's linked to non-existent element.
|
||||
"""
|
||||
ifc = IfcStore.get_file()
|
||||
if not ifc:
|
||||
if not ifc or not obj:
|
||||
return
|
||||
|
||||
props = None
|
||||
@@ -110,6 +110,8 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
props = obj.BIMObjectProperties
|
||||
elif isinstance(obj, bpy.types.Material):
|
||||
props = obj.BIMStyleProperties
|
||||
else:
|
||||
props = obj.BIMMeshProperties
|
||||
|
||||
if props and (ifc_definition_id := props.ifc_definition_id):
|
||||
try:
|
||||
|
||||
@@ -84,13 +84,12 @@ class Loader(bonsai.core.tool.Loader):
|
||||
@classmethod
|
||||
def get_mesh_name_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> str:
|
||||
representation_id = cls.get_representation_id_from_shape(geometry)
|
||||
representation = tool.Ifc.get().by_id(representation_id)
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
return cls.get_mesh_name(context_id, representation_id)
|
||||
return cls.get_mesh_name(tool.Ifc.get().by_id(representation_id))
|
||||
|
||||
@classmethod
|
||||
def get_mesh_name(cls, context_id: int, representation_id: int) -> str:
|
||||
return "{}/{}".format(context_id, representation_id)
|
||||
def get_mesh_name(cls, representation: ifcopenshell.entity_instance) -> str:
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
return "{}/{}".format(context_id, representation.id())
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, element: ifcopenshell.entity_instance) -> str:
|
||||
|
||||
@@ -55,28 +55,38 @@ class Root(bonsai.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def copy_representation(cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance) -> None:
|
||||
def copy_representation(
|
||||
cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance
|
||||
) -> dict[int, ifcopenshell.entity_instance]:
|
||||
def exclude_callback(attribute):
|
||||
return attribute.is_a("IfcProfileDef") and attribute.ProfileName
|
||||
|
||||
copied_entities: dict[int, ifcopenshell.entity_instance] = {}
|
||||
|
||||
if dest.is_a("IfcProduct"):
|
||||
if not source.Representation:
|
||||
return
|
||||
return copied_entities
|
||||
dest.Representation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(),
|
||||
source.Representation,
|
||||
exclude=["IfcGeometricRepresentationContext"],
|
||||
exclude_callback=exclude_callback,
|
||||
copied_entities=copied_entities,
|
||||
)
|
||||
elif dest.is_a("IfcTypeProduct"):
|
||||
if not source.RepresentationMaps:
|
||||
return
|
||||
return copied_entities
|
||||
dest.RepresentationMaps = [
|
||||
ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(), m, exclude=["IfcGeometricRepresentationContext"], exclude_callback=exclude_callback
|
||||
tool.Ifc.get(),
|
||||
m,
|
||||
exclude=["IfcGeometricRepresentationContext"],
|
||||
exclude_callback=exclude_callback,
|
||||
copied_entities=copied_entities,
|
||||
)
|
||||
for m in source.RepresentationMaps
|
||||
]
|
||||
return copied_entities
|
||||
|
||||
@classmethod
|
||||
def does_type_have_representations(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user