Fix bug where switching geometry may not correctly share representations

This commit is contained in:
Dion Moult
2024-09-18 21:08:10 +10:00
parent 3bdbb3d513
commit 0899a2a87b
3 changed files with 103 additions and 107 deletions
@@ -295,35 +295,22 @@ class SwitchRepresentation(bpy.types.Operator, tool.Ifc.Operator):
return False return False
def _execute(self, context): def _execute(self, context):
target_representation = tool.Ifc.get().by_id(self.ifc_definition_id) context = tool.Ifc.get().by_id(self.ifc_definition_id).ContextOfItems
target = target_representation.ContextOfItems for obj in tool.Blender.get_selected_objects():
is_subcontext = target.is_a("IfcGeometricRepresentationSubContext") if (
for obj in set(context.selected_objects + [context.active_object]): (element := tool.Ifc.get_entity(obj))
element = tool.Ifc.get_entity(obj) and obj.mode == "OBJECT"
if not element: and (representation := ifcopenshell.util.representation.get_representation(element, context))
continue ):
if not obj.mode == "OBJECT": core.switch_representation(
continue tool.Ifc,
if obj == context.active_object: tool.Geometry,
representation = target_representation obj=obj,
else: representation=representation,
if is_subcontext: should_reload=self.should_reload,
representation = ifcopenshell.util.representation.get_representation( is_global=self.should_switch_all_meshes,
element, target.ContextType, target.ContextIdentifier, target.TargetView should_sync_changes_first=True,
) )
else:
representation = ifcopenshell.util.representation.get_representation(element, target.ContextType)
if not representation:
continue
core.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=representation,
should_reload=self.should_reload,
is_global=self.should_switch_all_meshes,
should_sync_changes_first=True,
)
class RemoveRepresentation(bpy.types.Operator, tool.Ifc.Operator): class RemoveRepresentation(bpy.types.Operator, tool.Ifc.Operator):
+2 -36
View File
@@ -121,44 +121,10 @@ def switch_representation(
if not geometry.does_representation_id_exist(representation_id): if not geometry.does_representation_id_exist(representation_id):
return return
entity = ifc.get_entity(obj) if not geometry.get_object_data(obj) and geometry.is_text_literal(representation):
assert entity
current_obj_data = geometry.get_object_data(obj)
if not current_obj_data and geometry.is_text_literal(representation):
return return
use_immediate_repr = geometry.should_use_immediate_representation(entity, apply_openings) geometry.reimport_element_representations(obj, representation, apply_openings=apply_openings)
if use_immediate_repr:
# if it has openings make sure to switch to element's mapped representation
representation = geometry.unresolve_type_representation(representation, entity)
else:
# doesn't resolve mapped representations in case if it's going to have openings
# otherwise we would also add openings to the type and other occurences mesh data
representation = geometry.resolve_mapped_representation(representation)
old_repr_data = geometry.get_representation_data(representation)
if should_reload or not old_repr_data:
new_repr_data = geometry.import_representation(obj, representation, apply_openings=apply_openings)
geometry.rename_object(new_repr_data, geometry.get_representation_name(representation))
geometry.link(representation, new_repr_data)
else:
new_repr_data = old_repr_data
geometry.change_object_data(obj, new_repr_data, is_global=is_global and not use_immediate_repr)
geometry.record_object_materials(obj)
# we assume that all the occurences and the type have the same representation context active
# so geometry.delete_data cannot remove the data that's still used by some other object
if should_reload and old_repr_data:
# if current object was using some temporary mesh (like during profile edit mode) instead of `old_repr_data`
# then `change_object_data` won't switch the mesh for all the occurences and we need to do it explicitly
if current_obj_data != old_repr_data and geometry.has_data_users(old_repr_data):
geometry.replace_object_data_globally(old_repr_data, new_repr_data)
geometry.delete_data(old_repr_data)
geometry.clear_modifiers(obj)
geometry.clear_cache(entity)
def get_representation_ifc_parameters( def get_representation_ifc_parameters(
+85 -42
View File
@@ -603,65 +603,108 @@ class Geometry(bonsai.core.tool.Geometry):
return False return False
@classmethod @classmethod
def import_representation( def reimport_element_representations(
cls, obj: bpy.types.Object, representation: ifcopenshell.entity_instance, apply_openings: bool = True cls, obj: bpy.types.Object, representation: ifcopenshell.entity_instance, apply_openings: bool = True
) -> Union[bpy.types.Mesh, bpy.types.Curve]: ) -> Union[bpy.types.Mesh, bpy.types.Curve]:
element = tool.Ifc.get_entity(obj)
assert element
elements = set()
element_types = set()
representation = ifcopenshell.util.representation.resolve_representation(representation)
context = representation.ContextOfItems
for mapped_element in ifcopenshell.util.element.get_elements_by_representation(tool.Ifc.get(), representation):
if mapped_element.is_a("IfcTypeProduct"):
element_types.add(mapped_element)
else:
elements.add(mapped_element)
if element_type := ifcopenshell.util.element.get_type(mapped_element):
element_types.add(element_type)
logger = logging.getLogger("ImportIFC") logger = logging.getLogger("ImportIFC")
ifc_import_settings = bonsai.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger) ifc_import_settings = bonsai.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
element = tool.Ifc.get_entity(obj)
assert element # Type checker.
settings = ifcopenshell.geom.settings() settings = ifcopenshell.geom.settings()
settings.set("weld-vertices", True) settings.set("weld-vertices", True)
settings.set("apply-default-materials", False) settings.set("apply-default-materials", False)
settings.set("layerset-first", True) settings.set("layerset-first", True)
settings.set("keep-bounding-boxes", True) settings.set("keep-bounding-boxes", True)
context = representation.ContextOfItems settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
ifc_importer = bonsai.bim.import_ifc.IfcImporter(ifc_import_settings) ifc_importer = bonsai.bim.import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = tool.Ifc.get() ifc_importer.file = tool.Ifc.get()
# create_shape doesn't support point cloud representations. # TODO support fallbacks like for point clouds
if representation.RepresentationType in ("PointCloud", "Point"):
mesh = tool.Loader.create_point_cloud_mesh(representation)
if mesh is None:
raise Exception(f"Failed to process point cloud representation: {representation}.")
return mesh
if element.is_a("IfcTypeProduct"): settings.set("context-ids", [context.id()])
# You may only specify a single representation when creating shapes for types if not apply_openings:
try: settings.set("disable-opening-subtractions", True)
shape = ifcopenshell.geom.create_shape(settings, representation)
except:
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, representation)
else:
if not apply_openings:
settings.set("disable-opening-subtractions", True)
if context.ContextIdentifier == "Body" and context.TargetView == "MODEL_VIEW": shape = None
try: iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=elements)
shape = ifcopenshell.geom.create_shape(settings, element, representation) meshes = {}
except: if iterator.initialize():
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS) while True:
shape = ifcopenshell.geom.create_shape(settings, element, representation) shape = iterator.get()
else: element = tool.Ifc.get().by_id(shape.id)
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS) if obj := tool.Ifc.get_object(element):
shape = ifcopenshell.geom.create_shape(settings, element, representation) mesh_name = tool.Loader.get_mesh_name_from_shape(shape.geometry)
mesh = meshes.get(mesh_name)
if mesh is None:
# Duplicate code
representation = tool.Ifc.get().by_id(int(shape.geometry.id.split("-")[0]))
if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
mesh = tool.Loader.create_camera(element, representation, shape)
if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element):
mesh = ifc_importer.create_curve(element, shape)
elif shape:
mesh = ifc_importer.create_mesh(element, shape)
ifc_importer.material_creator.load_existing_materials()
shape_has_openings = cls.does_shape_has_openings(shape)
ifc_importer.material_creator.create(element, obj, mesh, shape_has_openings)
mesh.BIMMeshProperties.has_openings_applied = apply_openings
if not shape_has_openings:
tool.Loader.load_indexed_colour_map(representation, mesh)
tool.Loader.link_mesh(shape, mesh)
meshes[mesh_name] = mesh
if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": old_mesh = obj.data
mesh = tool.Loader.create_camera(element, representation, shape) cls.change_object_data(obj, mesh, is_global=False)
if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element): cls.record_object_materials(obj)
mesh = ifc_importer.create_curve(element, shape) if not cls.has_data_users(old_mesh):
elif shape: cls.delete_data(old_mesh)
mesh = ifc_importer.create_mesh(element, shape) cls.clear_modifiers(obj)
ifc_importer.material_creator.load_existing_materials() cls.clear_cache(element)
shape_has_openings = cls.does_shape_has_openings(shape)
ifc_importer.material_creator.create(element, obj, mesh, shape_has_openings)
mesh.BIMMeshProperties.has_openings_applied = apply_openings
if not shape_has_openings:
tool.Loader.load_indexed_colour_map(representation, mesh)
return mesh if not iterator.next():
break
for element in element_types:
if obj := tool.Ifc.get_object(element):
if representation := ifcopenshell.util.representation.get_representation(element, context):
geometry = ifcopenshell.geom.create_shape(settings, representation)
mesh_name = tool.Loader.get_mesh_name_from_shape(geometry)
mesh = meshes.get(mesh_name)
if mesh is None:
# Duplicate code
representation = tool.Ifc.get().by_id(int(geometry.id.split("-")[0]))
if geometry:
mesh = ifc_importer.create_mesh(element, geometry)
ifc_importer.material_creator.load_existing_materials()
shape_has_openings = False
ifc_importer.material_creator.create(element, obj, mesh, shape_has_openings)
mesh.BIMMeshProperties.has_openings_applied = apply_openings
if not shape_has_openings:
tool.Loader.load_indexed_colour_map(representation, mesh)
tool.Loader.link_mesh(geometry, mesh)
meshes[mesh_name] = mesh
old_mesh = obj.data
cls.change_object_data(obj, mesh, is_global=False)
cls.record_object_materials(obj)
if not cls.has_data_users(old_mesh):
cls.delete_data(old_mesh)
cls.clear_modifiers(obj)
cls.clear_cache(element)
@classmethod @classmethod
def does_shape_has_openings( def does_shape_has_openings(