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
def _execute(self, context):
target_representation = tool.Ifc.get().by_id(self.ifc_definition_id)
target = target_representation.ContextOfItems
is_subcontext = target.is_a("IfcGeometricRepresentationSubContext")
for obj in set(context.selected_objects + [context.active_object]):
element = tool.Ifc.get_entity(obj)
if not element:
continue
if not obj.mode == "OBJECT":
continue
if obj == context.active_object:
representation = target_representation
else:
if is_subcontext:
representation = ifcopenshell.util.representation.get_representation(
element, target.ContextType, target.ContextIdentifier, target.TargetView
)
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,
)
context = tool.Ifc.get().by_id(self.ifc_definition_id).ContextOfItems
for obj in tool.Blender.get_selected_objects():
if (
(element := tool.Ifc.get_entity(obj))
and obj.mode == "OBJECT"
and (representation := ifcopenshell.util.representation.get_representation(element, context))
):
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):
+2 -36
View File
@@ -121,44 +121,10 @@ def switch_representation(
if not geometry.does_representation_id_exist(representation_id):
return
entity = ifc.get_entity(obj)
assert entity
current_obj_data = geometry.get_object_data(obj)
if not current_obj_data and geometry.is_text_literal(representation):
if not geometry.get_object_data(obj) and geometry.is_text_literal(representation):
return
use_immediate_repr = geometry.should_use_immediate_representation(entity, 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)
geometry.reimport_element_representations(obj, representation, apply_openings=apply_openings)
def get_representation_ifc_parameters(
+85 -42
View File
@@ -603,65 +603,108 @@ class Geometry(bonsai.core.tool.Geometry):
return False
@classmethod
def import_representation(
def reimport_element_representations(
cls, obj: bpy.types.Object, representation: ifcopenshell.entity_instance, apply_openings: bool = True
) -> 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")
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.set("weld-vertices", True)
settings.set("apply-default-materials", False)
settings.set("layerset-first", 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.file = tool.Ifc.get()
# create_shape doesn't support point cloud representations.
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
# TODO support fallbacks like for point clouds
if element.is_a("IfcTypeProduct"):
# You may only specify a single representation when creating shapes for types
try:
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)
settings.set("context-ids", [context.id()])
if not apply_openings:
settings.set("disable-opening-subtractions", True)
if context.ContextIdentifier == "Body" and context.TargetView == "MODEL_VIEW":
try:
shape = ifcopenshell.geom.create_shape(settings, element, representation)
except:
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, element, representation)
else:
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, element, representation)
shape = None
iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=elements)
meshes = {}
if iterator.initialize():
while True:
shape = iterator.get()
element = tool.Ifc.get().by_id(shape.id)
if obj := tool.Ifc.get_object(element):
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":
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)
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)
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
def does_shape_has_openings(