diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 24cc944e8a..312babafa2 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -133,8 +133,9 @@ if bpy is not None: operator.SmartClashGroup, operator.SelectSmartGroup, operator.LoadSmartGroupsForActiveClashSet, - operator.SwitchContext, - operator.RemoveContext, + operator.AddRepresentation, + operator.SwitchRepresentation, + operator.RemoveRepresentation, operator.OpenUpstream, operator.BIM_OT_ChangeClassificationLevel, operator.AddPropertySetTemplate, diff --git a/src/ifcblenderexport/blenderbim/bim/export_ifc.py b/src/ifcblenderexport/blenderbim/bim/export_ifc.py index 0e755ec314..6da3335d67 100644 --- a/src/ifcblenderexport/blenderbim/bim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/export_ifc.py @@ -1056,29 +1056,32 @@ class IfcParser: for context in self.ifc_export_settings.context_tree: for subcontext in context["subcontexts"]: for target_view in subcontext["target_views"]: - rep_context = self.get_obj_representation_context(obj, context["name"], subcontext["name"], target_view) - if rep_context: - self.append_representation_in_context(obj, rep_context, name) + representation = self.get_shape_representation(obj, context["name"], subcontext["name"], target_view) + if representation: + self.append_representation_in_context(obj, representation, name) - def get_obj_representation_context(self, obj, context, subcontext, target_view): - for c in obj.BIMObjectProperties.representation_contexts: - if c.context == context and c.name == subcontext and c.target_view == target_view: - return c - if obj.BIMObjectProperties.representation_contexts: + def get_shape_representation(self, obj, context, subcontext, target_view): + for representation in obj.BIMObjectProperties.representations: + c = self.stored_file.by_id(representation.ifc_definition_id) + if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view: + return representation + if obj.BIMObjectProperties.representations: return - if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW": - representation_context = obj.BIMObjectProperties.representation_contexts.add() - representation_context.context = "Model" - representation_context.name = "Body" - representation_context.target_view = "MODEL_VIEW" - return representation_context + # TODO: reimplement - see bug #1222 + #if context == "Model" and subcontext == "Body" and target_view == "MODEL_VIEW": + # representation_context = obj.BIMObjectProperties.representation_contexts.add() + # representation_context.context = "Model" + # representation_context.name = "Body" + # representation_context.target_view = "MODEL_VIEW" + # return representation_context - def append_representation_in_context(self, obj, rep_context, name): - context = rep_context.context - subcontext = rep_context.name - target_view = rep_context.target_view + def append_representation_in_context(self, obj, shape_representation, name): + context_of_items = self.stored_file.by_id(shape_representation.ifc_definition_id).ContextOfItems + context = context_of_items.ContextType + subcontext = context_of_items.ContextIdentifier + target_view = context_of_items.TargetView - if self.ifc_export_settings.should_roundtrip_native and rep_context.ifc_definition_id: + if self.ifc_export_settings.should_roundtrip_native and shape_representation.ifc_definition_id: self.representations[ "{}/{}/{}/{}".format(context, subcontext, target_view, name) ] = self.get_representation(obj.data, obj, context, subcontext, target_view) @@ -1087,7 +1090,6 @@ class IfcParser: context_prefix = "/".join([context, subcontext, target_view]) mesh_name = "/".join([context_prefix, name]) mesh = self.search_for_mesh_or_curve_data(mesh_name) - # TODO: if the search result is empty, we should check for ifc_definition_id if mesh: self.representations[mesh_name] = self.get_representation(mesh, obj, context, subcontext, target_view) if "Model/Box/MODEL_VIEW" in self.generated_subcontexts and context_prefix == "Model/Body/MODEL_VIEW": @@ -1112,7 +1114,7 @@ class IfcParser: return data def get_representation(self, mesh, obj, context, subcontext, target_view): - rep_context = self.get_obj_representation_context(obj, context, subcontext, target_view) + representation = self.get_shape_representation(obj, context, subcontext, target_view) return { "ifc": None, "raw": mesh, @@ -1120,9 +1122,9 @@ class IfcParser: "context": context, "subcontext": subcontext, "target_view": target_view, - "has_ifc_definition": rep_context and rep_context.ifc_definition_id, + "has_ifc_definition": representation and representation.ifc_definition_id, "ifc_definition": mesh.BIMMeshProperties.ifc_definition if hasattr(mesh, "BIMMeshProperties") else None, - "ifc_definition_id": rep_context.ifc_definition_id if rep_context else 0 + "ifc_definition_id": representation.ifc_definition_id if representation else 0 if hasattr(mesh, "BIMMeshProperties") else None, "is_parametric": mesh.BIMMeshProperties.is_parametric if hasattr(mesh, "BIMMeshProperties") else False, @@ -1344,8 +1346,9 @@ class IfcExporter: self.roundtrip_id_new_to_old = {} def export(self, selected_objects): - self.file = ifc.IfcStore.get_file() - if self.file and self.ifc_export_settings.should_export_from_memory: + self.stored_file = ifc.IfcStore.get_file() # See bug #1222 + if self.stored_file and self.ifc_export_settings.should_export_from_memory: + self.file = self.stored_file return self.write_ifc_file() self.schema_version = self.ifc_export_settings.schema self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema_version) @@ -2038,8 +2041,8 @@ class IfcExporter: # At the moment, we assume that styled items only apply to the body context. if representation.RepresentationIdentifier != "Body": continue - rep_context = self.ifc_parser.get_obj_representation_context(product["raw"], "Model", "Body", "MODEL_VIEW") - if self.ifc_export_settings.should_roundtrip_native and rep_context and rep_context.ifc_definition_id: + rep = self.ifc_parser.get_shape_representation(product["raw"], "Model", "Body", "MODEL_VIEW") + if self.ifc_export_settings.should_roundtrip_native and rep and rep.ifc_definition_id: # For native roundtripping, each slot could be a one to many relationship to items for item in self.get_geometric_representation_items(representation): original_id = self.roundtrip_id_new_to_old[item.id()] diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index bbe4fa7d41..3243c5e96a 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -773,6 +773,7 @@ class IfcImporter: obj = self.existing_elements[element.GlobalId] else: obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None) + obj.BIMObjectProperties.ifc_definition_id = element.id() self.add_element_attributes(element, obj.BIMObjectProperties) group_collection.objects.link(obj) self.groups[element.GlobalId] = {"ifc": element, "blender": obj} @@ -809,6 +810,7 @@ class IfcImporter: shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve) mesh = self.create_mesh(axis, shape) obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh) + obj.BIMObjectProperties.ifc_definition_id = element.id() obj.matrix_world = matrix_world self.add_element_attributes(axis, obj.BIMObjectProperties) grid.objects.link(obj) @@ -843,6 +845,7 @@ class IfcImporter: except: self.ifc_import_settings.logger.error("Failed to generate shape for %s", element) obj = bpy.data.objects.new(self.get_name(element), mesh) + obj.BIMObjectProperties.ifc_definition_id = element.id() self.material_creator.create(element, obj, mesh) self.add_element_attributes(element, obj.BIMObjectProperties) self.add_element_classifications(element, obj) @@ -955,6 +958,7 @@ class IfcImporter: mesh = None obj = bpy.data.objects.new(self.get_name(element), mesh) + obj.BIMObjectProperties.ifc_definition_id = element.id() if shape: m = shape.transformation.matrix.data @@ -1760,6 +1764,7 @@ class IfcImporter: element = rel_aggregate.RelatingObject obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None) + obj.BIMObjectProperties.ifc_definition_id = element.id() obj.instance_type = "COLLECTION" obj.instance_collection = collection self.place_object_in_spatial_tree(element, obj) @@ -1823,6 +1828,7 @@ class IfcImporter: return obj = bpy.data.objects.new(self.get_name(element), mesh) + obj.BIMObjectProperties.ifc_definition_id = element.id() self.material_creator.create(element, obj, mesh) obj.matrix_world = self.get_element_matrix(element, mesh_name) self.add_element_attributes(element, obj.BIMObjectProperties) diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index a9ee97db61..08df3d915e 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -10,6 +10,7 @@ import subprocess import ifcopenshell import ifcopenshell.util.selector import ifcopenshell.util.geolocation +import ifcopenshell.util.element import ifcopenshell.util.schema import tempfile from . import export_ifc @@ -2156,6 +2157,7 @@ class CutSection(bpy.types.Operator): bl_label = "Cut Section" def execute(self, context): + self.file = ifc.IfcStore.get_file() camera = bpy.context.scene.camera if not (camera.type == "CAMERA" and camera.data.type == "ORTHO"): return {"FINISHED"} @@ -2322,11 +2324,10 @@ class CutSection(bpy.types.Operator): bpy.data.objects[name].hide_set(value) def does_obj_have_target_view_representation(self, obj, camera): - return camera.data.BIMCameraProperties.target_view in [ - c.target_view - for c in obj.BIMObjectProperties.representation_contexts - if c.context == "Plan" and c.name == "Annotation" - ] + for representation in obj.BIMObjectProperties.representations: + element = self.file.by_id(representation.ifc_definition_id) + if ifcopenshell.util.element.is_representation_of_context(element, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view): + return True def is_landscape(self): return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y @@ -2464,109 +2465,45 @@ class ActivateView(bpy.types.Operator): return {"FINISHED"} -class SwitchContext(bpy.types.Operator): - bl_idname = "bim.switch_context" - bl_label = "Switch Context" - has_target_context: bpy.props.BoolProperty() - context_name: bpy.props.StringProperty() - subcontext_name: bpy.props.StringProperty() - target_view_name: bpy.props.StringProperty() +class AddRepresentation(bpy.types.Operator): + bl_idname = "bim.add_representation" + bl_label = "Add Representation" - # Warning: This is an incredibly experimental operator. It effectively does - # a mini-import. A better approach will make this obsolete in the future. def execute(self, context): self.obj = bpy.context.active_object + self.file = ifc.IfcStore.get_file() if "/" not in self.obj.data.name: self.obj.data.name = ifcopenshell.guid.compress(str(uuid.uuid4()).replace("-", "")) self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name - has_default_context = False - for subcontext in self.obj.BIMObjectProperties.representation_contexts: - if ( - subcontext.context == "Model" - and subcontext.name == "Body" - and subcontext.target_view == "MODEL_VIEW" - ): - has_default_context = True - break - if not has_default_context: - representation_context = self.obj.BIMObjectProperties.representation_contexts.add() - representation_context.context = "Model" - representation_context.name = "Body" - representation_context.target_view = "MODEL_VIEW" self.context = bpy.context.scene.BIMProperties.available_contexts self.subcontext = bpy.context.scene.BIMProperties.available_subcontexts self.target_view = bpy.context.scene.BIMProperties.available_target_views - if self.has_target_context: - self.context = self.context_name - self.subcontext = self.subcontext_name - self.target_view = self.target_view_name - existing_mesh = self.obj.data existing_mesh.use_fake_user = True - mesh = bpy.data.meshes.get( - "{}/{}/{}/{}".format(self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]) + mesh = self.obj.data.copy() + mesh.name = "{}/{}/{}/{}".format( + self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3] ) - if not mesh: - try: - global_id = self.obj.BIMObjectProperties.attributes.get("GlobalId").string_value - mesh = self.pull_mesh_from_ifc(global_id) - except: - mesh = self.obj.data.copy() - mesh.name = "{}/{}/{}/{}".format( - self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3] - ) - has_context = False - for context in self.obj.BIMObjectProperties.representation_contexts: - if ( - context.context == self.context - and context.name == self.subcontext - and context.target_view == self.target_view - ): - has_context = True - break - if not has_context: - representation_context = self.obj.BIMObjectProperties.representation_contexts.add() - representation_context.context = self.context - representation_context.name = self.subcontext - representation_context.target_view = self.target_view - mesh.use_fake_user = True + representation = self.obj.BIMObjectProperties.representations.add() + representation.name = self.subcontext + representation.type = "Brep" if self.file.schema == "IFC2X3" else "Tessellation" + mesh.use_fake_user = True self.obj.data = mesh + + # TODO: push representation return {"FINISHED"} - def pull_mesh_from_ifc(self, global_id): + def push_mesh_to_ifc(self): self.file = ifc.IfcStore.get_file() logger = logging.getLogger("ImportIFC") ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger) - element = self.file.by_id(global_id) + element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id) settings = ifcopenshell.geom.settings() settings.set(settings.INCLUDE_CURVES, True) - if element.is_a("IfcProduct"): - representations = element.Representation.Representations - else: - representations = element.RepresentationMaps - - for rep in element.Representation.Representations: - if ( - rep.ContextOfItems.is_a("IfcGeometricRepresentationSubContext") - and rep.ContextOfItems.ContextType == self.context - and rep.ContextOfItems.ContextIdentifier == self.subcontext - and rep.ContextOfItems.TargetView == self.target_view - ): - break - elif ( - rep.ContextOfItems.is_a("IfcGeometricRepresentationContext") - and rep.ContextOfItems.ContextType == self.context - and rep.ContextOfItems.ContextIdentifier == self.subcontext - ): - break - - if not element.is_a("IfcProduct"): - rep = rep.MappedRepresentation - - shape = ifcopenshell.geom.create_shape(settings, rep) + shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id)) ifc_importer = import_ifc.IfcImporter(ifc_import_settings) ifc_importer.file = self.file mesh = ifc_importer.create_mesh(element, shape) @@ -2574,37 +2511,94 @@ class SwitchContext(bpy.types.Operator): self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3] ) self.obj.data = mesh - material_creator = import_ifc.MaterialCreator(ifc_import_settings) + material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer) material_creator.create(element, self.obj, mesh) return mesh -class RemoveContext(bpy.types.Operator): - bl_idname = "bim.remove_context" - bl_label = "Remove Context" +class SwitchRepresentation(bpy.types.Operator): + bl_idname = "bim.switch_representation" + bl_label = "Switch Representation" + ifc_definition_id: bpy.props.IntProperty() + + def execute(self, context): + self.obj = bpy.context.active_object + + self.file = ifc.IfcStore.get_file() + if "/" not in self.obj.data.name: + self.obj.data.name = ifcopenshell.guid.compress(str(uuid.uuid4()).replace("-", "")) + self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name + + context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems + self.context = context_of_items.ContextType + self.subcontext = context_of_items.ContextIdentifier + self.target_view = context_of_items.TargetView + + existing_mesh = self.obj.data + existing_mesh.use_fake_user = True + mesh = bpy.data.meshes.get( + "{}/{}/{}/{}".format(self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]) + ) + if not mesh: + mesh = self.pull_mesh_from_ifc() + mesh.use_fake_user = True + self.obj.data = mesh + return {"FINISHED"} + + def pull_mesh_from_ifc(self): + self.file = ifc.IfcStore.get_file() + logger = logging.getLogger("ImportIFC") + ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger) + element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id) + settings = ifcopenshell.geom.settings() + settings.set(settings.INCLUDE_CURVES, True) + shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id)) + ifc_importer = import_ifc.IfcImporter(ifc_import_settings) + ifc_importer.file = self.file + mesh = ifc_importer.create_mesh(element, shape) + mesh.name = "{}/{}/{}/{}".format( + self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3] + ) + self.obj.data = mesh + material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer) + material_creator.create(element, self.obj, mesh) + return mesh + + +class RemoveRepresentation(bpy.types.Operator): + bl_idname = "bim.remove_representation" + bl_label = "Remove Representation" index: bpy.props.IntProperty() def execute(self, context): + # TODO This should be refactored into a testable agnostic module. See #1222. + # Prep work + self.file = ifc.IfcStore.get_file() obj = bpy.context.active_object - data = obj.BIMObjectProperties.representation_contexts[self.index] + representation = obj.BIMObjectProperties.representations[self.index] + element = self.file.by_id(representation.ifc_definition_id) + c = element.ContextOfItems + # Blender work if "/" not in obj.data.name: obj.data.name = "Model/Body/MODEL_VIEW/" + obj.data.name - mesh = bpy.data.meshes.get( - "{}/{}/{}/{}".format(data.context, data.name, data.target_view, obj.data.name.split("/")[3]) + "{}/{}/{}/{}".format(c.ContextType, c.ContextIdentifier, c.TargetView, obj.data.name.split("/")[3]) ) - if mesh: if obj.data == mesh: + # TODO we can do better than this void_name = "Void/Void/Void/" + obj.data.name.split("/")[3] void_mesh = bpy.data.meshes.get(void_name) if not void_mesh: void_mesh = bpy.data.meshes.new(void_name) obj.data = void_mesh bpy.data.meshes.remove(mesh) + obj.BIMObjectProperties.representations.remove(self.index) - obj.BIMObjectProperties.representation_contexts.remove(self.index) + # IFC work + # TODO: this works as a MVP but leaves a bunch of junk in the file. See #1222. + self.file.remove(element) return {"FINISHED"} @@ -4427,6 +4421,7 @@ class GetRepresentationIfcParameters(bpy.types.Operator): bl_label = "Get Representation IFC Parameters" def execute(self, context): + self.file = ifc.IfcStore.get_file() obj = bpy.context.active_object props = obj.data.BIMMeshProperties element_id = self.get_ifc_definition_id(obj) @@ -4452,9 +4447,10 @@ class GetRepresentationIfcParameters(bpy.types.Operator): context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW") else: context, subcontext, target_view = obj.data.name.split("/")[0:3] - for c in obj.BIMObjectProperties.representation_contexts: - if c.context == context and c.name == subcontext and c.target_view == target_view: - return c.ifc_definition_id or None + for representation in obj.BIMObjectProperties.representations: + element = self.file.by_id(representation.ifc_definition_id) + if ifcopenshell.util.element.is_representation_of_context(element, context, subcontext, target_view): + return representation.ifc_definition_id or None class BakeParametricGeometry(bpy.types.Operator): @@ -4462,14 +4458,16 @@ class BakeParametricGeometry(bpy.types.Operator): bl_label = "Bake Parametric Geometry" def execute(self, context): - obj = bpy.context.active_object - if "/" not in obj.data.name: - context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW") - else: - context, subcontext, target_view = obj.data.name.split("/")[0:3] - for c in obj.BIMObjectProperties.representation_contexts: - if c.context == context and c.name == subcontext and c.target_view == target_view: - c.ifc_definition_id = 0 + # TODO rewrite, see #1222 + # obj = bpy.context.active_object + # if "/" not in obj.data.name: + # context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW") + # else: + # context, subcontext, target_view = obj.data.name.split("/")[0:3] + # for representation in obj.BIMObjectProperties.representations: + # element = self.file.by_id(representation.ifc_definition_id) + # if ifcopenshell.util.element.is_representation_of_context(element, context, subcontext, target_view): + # representation.ifc_definition_id = 0 return {"FINISHED"} @@ -4479,6 +4477,7 @@ class UpdateIfcRepresentation(bpy.types.Operator): index: bpy.props.IntProperty() def execute(self, context): + self.file = ifc.IfcStore.get_file() props = bpy.context.active_object.data.BIMMeshProperties parameter = props.ifc_parameters[self.index] element = ifc.IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value @@ -4505,9 +4504,10 @@ class UpdateIfcRepresentation(bpy.types.Operator): context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW") else: context, subcontext, target_view = obj.data.name.split("/")[0:3] - for c in obj.BIMObjectProperties.representation_contexts: - if c.context == context and c.name == subcontext and c.target_view == target_view: - return c.ifc_definition_id or None + for representation in obj.BIMObjectProperties.representations: + c = self.file.by_id(self.ifc_definition_id).ContextOfItems + if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view: + return representation.ifc_definition_id or None class BlenderClasher: diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index 5da7176506..cf5cd15994 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -1480,6 +1480,7 @@ class BoundaryCondition(PropertyGroup): class BIMObjectProperties(PropertyGroup): + ifc_definition_id: IntProperty(name="IFC Definition ID") is_reassigning_class: BoolProperty(name="Is Reassigning Class") global_ids: CollectionProperty(name="GlobalIds", type=GlobalId) attributes: CollectionProperty(name="Attributes", type=Attribute) @@ -1544,6 +1545,7 @@ class ItemSlotMap(PropertyGroup): class BIMMeshProperties(PropertyGroup): + ifc_definition_id: IntProperty(name="IFC Definition ID") is_native: BoolProperty(name="Is Native", default=False) is_swept_solid: BoolProperty(name="Is Swept Solid") swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid) @@ -1553,4 +1555,4 @@ class BIMMeshProperties(PropertyGroup): ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter) active_representation_item_index: IntProperty(name="Active Representation Item Index") presentation_layer_index: IntProperty(name="Presentation Layer Index", default=-1) - ifc_item_ids: CollectionProperty(name="IFC Definition ID", type=ItemSlotMap) + ifc_item_ids: CollectionProperty(name="IFC Item IDs", type=ItemSlotMap) diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index 90ed6a32f4..244b94eb0d 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -645,9 +645,7 @@ class BIM_PT_representations(Panel): row.prop(bpy.context.scene.BIMProperties, "available_contexts", text="") row.prop(bpy.context.scene.BIMProperties, "available_subcontexts", text="") row.prop(bpy.context.scene.BIMProperties, "available_target_views", text="") - # TODO: reimplement with incremental editing - # op = row.operator("bim.switch_context", icon="ADD", text="") - # op.has_target_context = False + row.operator("bim.add_representation", icon="ADD", text="") self.file = ifc.IfcStore.get_file() for index, representation in enumerate(props.representations): @@ -656,16 +654,12 @@ class BIM_PT_representations(Panel): row.prop(representation, "type", text="") if not representation.ifc_definition_id: continue - subcontext = self.file.by_id(representation.ifc_definition_id).ContextOfItems - if subcontext.is_a() == "IfcGeometricRepresentationContext": + context_of_items = self.file.by_id(representation.ifc_definition_id).ContextOfItems + if context_of_items.is_a() == "IfcGeometricRepresentationContext": continue - # TODO: reimplement with incremental editing - # op = row.operator("bim.switch_context", icon="OUTLINER_DATA_MESH", text="") - # op.has_target_context = True - # op.context_name = subcontext.ContextType - # op.subcontext_name = subcontext.ContextIdentifier - # op.target_view_name = subcontext.TargetView - # row.operator("bim.remove_context", icon="X", text="").index = index + op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="") + op.ifc_definition_id = representation.ifc_definition_id + row.operator("bim.remove_representation", icon="X", text="").index = index class BIM_PT_classification_references(Panel): diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 65cc386e59..b67ec58cf3 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -93,3 +93,32 @@ def replace_attribute(element, old, new): if item == old: new_attribute[j] = new element[i] = new_attribute + + +def is_representation_of_context(representation, context, subcontext=None, target_view=None): + if target_view is not None: + return ( + representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext") + and representation.ContextOfItems.TargetView == target_view + and representation.ContextOfItems.ContextIdentifier == subcontext + and representation.ContextOfItems.ContextType == context + ) + elif subcontext is not None: + return ( + representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext") + and representation.ContextOfItems.ContextIdentifier == subcontext + and representation.ContextOfItems.ContextType == context + ) + elif representation.ContextOfItems.ContextType == context: + return True + + +def get_representation(element, context, subcontext=None, target_view=None): + if element.is_a("IfcProduct") and element.Representation: + for r in element.Representation.Representations: + if is_representation_of_context(r, context, subcontext, target_view): + return r + elif element.is_a("IfcTypeProduct") and element.RepresentationMaps: + for r in element.RepresentationMaps: + if is_representation_of_context(r.MappedRepresentation, context, subcontext, target_view): + return r.MappedRepresentation