diff --git a/src/bonsai/bonsai/bim/module/drawing/__init__.py b/src/bonsai/bonsai/bim/module/drawing/__init__.py index 8b5a787ebe..eb5779d4af 100644 --- a/src/bonsai/bonsai/bim/module/drawing/__init__.py +++ b/src/bonsai/bonsai/bim/module/drawing/__init__.py @@ -143,6 +143,7 @@ def menu_func(self, context): if element and element.is_a("IfcAnnotation") and element.ObjectType in ["SECTION", "ELEVATION"]: self.layout.operator("bim.activate_drawing_by_annotation", text="Go to Drawing") + def register(): if not bpy.app.background: bpy.utils.register_tool(workspace.AnnotationTool, after={"bim.bim_tool"}, separator=True, group=False) @@ -155,7 +156,7 @@ def register(): bpy.app.handlers.load_post.append(handler.load_post) bpy.app.handlers.depsgraph_update_pre.append(handler.depsgraph_update_pre_handler) bpy.types.VIEW3D_MT_image_add.append(ui.add_object_button) - bpy.types.VIEW3D_MT_object_context_menu.append(menu_func) + bpy.types.VIEW3D_MT_object_context_menu.append(menu_func) def unregister(): diff --git a/src/bonsai/bonsai/bim/module/drawing/decoration.py b/src/bonsai/bonsai/bim/module/drawing/decoration.py index 3a1d73de16..a022af28d0 100644 --- a/src/bonsai/bonsai/bim/module/drawing/decoration.py +++ b/src/bonsai/bonsai/bim/module/drawing/decoration.py @@ -1655,7 +1655,7 @@ class CutDecorator: if isinstance(space, bpy.types.SpaceView3D) and space.local_view: in_local_view = True break - + # If just entering local view (transition from False to True) if in_local_view and not self.__class__.was_in_local_view: self.__class__.local_view_has_annotation = False @@ -1664,10 +1664,10 @@ class CutDecorator: if element and element.is_a("IfcAnnotation"): self.__class__.local_view_has_annotation = True break - + # Update the state for next time self.__class__.was_in_local_view = in_local_view - + # Skip decorations if in local view and no IfcAnnotation was selected when entering if in_local_view and not self.__class__.local_view_has_annotation: return @@ -2066,7 +2066,7 @@ class DecorationsHandler: if isinstance(space, bpy.types.SpaceView3D) and space.local_view: in_local_view = True break - + # If just entering local view (transition from False to True) if in_local_view and not self.__class__.was_in_local_view: self.__class__.local_view_has_annotation = False @@ -2075,10 +2075,10 @@ class DecorationsHandler: if element and element.is_a("IfcAnnotation"): self.__class__.local_view_has_annotation = True break - + # Update the state for next time self.__class__.was_in_local_view = in_local_view - + # Skip decorations if in local view and no IfcAnnotation was selected when entering if in_local_view and not self.__class__.local_view_has_annotation: return diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 0c5c99e623..aa8d0a317d 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -344,20 +344,20 @@ class CreateDrawing(bpy.types.Operator): # Clear any local camera setup and force viewport to use scene camera for area in context.screen.areas: - if area.type == 'VIEW_3D': + if area.type == "VIEW_3D": for space in area.spaces: - if space.type == 'VIEW_3D': + if space.type == "VIEW_3D": # Clear local camera to ensure we use scene.camera space.use_local_camera = False space.camera = context.scene.camera - space.region_3d.view_perspective = 'CAMERA' + space.region_3d.view_perspective = "CAMERA" print(f"Set viewport camera to: {context.scene.camera.name}") break - + # Force complete scene update context.view_layer.update() context.evaluated_depsgraph_get() - + underlay_svg = self.generate_underlay(context) with profile("Generate linework"): @@ -3069,9 +3069,9 @@ class AddReference(bpy.types.Operator, tool.Ifc.Operator, ImportHelper): filter_glob: bpy.props.StringProperty(default="*.svg", options={"HIDDEN"}) use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True) filename_ext = ".svg" - + files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement) - directory: bpy.props.StringProperty(subtype='DIR_PATH') + directory: bpy.props.StringProperty(subtype="DIR_PATH") def _execute(self, context): # Handle both single and multiple file selection @@ -4034,66 +4034,66 @@ class ExcludeAnnotation(bpy.types.Operator, tool.Ifc.Operator): tool.Drawing.exclude_annotation_from_drawing(referenced_element, drawing) core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing) + class ActivateDrawingByAnnotation(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.activate_drawing_by_annotation" bl_label = "Activate Drawing" bl_description = "Activate the drawing corresponding to the selected annotation" bl_options = {"REGISTER", "UNDO"} - + @classmethod def poll(cls, context): # Check if an annotation object is selected if not context.selected_objects: cls.poll_message_set("No object selected") return False - + active_obj = context.active_object if not active_obj: cls.poll_message_set("No active object") return False - + element = tool.Ifc.get_entity(active_obj) if not element: cls.poll_message_set("Selected object is not an IFC element") return False - + # Check if it's an IfcAnnotation with ObjectType = "SECTION" or "ELEVATION" if not element.is_a("IfcAnnotation") or element.ObjectType not in ["SECTION", "ELEVATION"]: cls.poll_message_set("Selected object is not a drawing annotation") return False - + return True def _execute(self, context): active_obj = context.active_object element = tool.Ifc.get_entity(active_obj) - + if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in ["SECTION", "ELEVATION"]: self.report({"ERROR"}, "Selected object is not a drawing annotation") return {"CANCELLED"} - + # Find the drawing/camera element that this annotation references drawing_element = self.find_drawing_from_annotation(element) - + if not drawing_element: self.report({"ERROR"}, "Could not find drawing element for this annotation") return {"CANCELLED"} - + # Use the existing ActivateDrawing operator with the drawing element's ID bpy.ops.bim.activate_drawing(drawing=drawing_element.id()) - + return {"FINISHED"} - + def find_drawing_from_annotation(self, annotation_element): """Find the drawing/camera element that this annotation references.""" ifc = tool.Ifc.get() - + # Check IfcRelAssignsToProduct relationships for rel in ifc.get_inverse(annotation_element): if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct: if rel.RelatingProduct.is_a("IfcAnnotation"): # Found the drawing element! return rel.RelatingProduct - - - return None \ No newline at end of file + + return None diff --git a/src/bonsai/bonsai/bim/module/model/opening.py b/src/bonsai/bonsai/bim/module/model/opening.py index d028425fb2..962cec8bc2 100644 --- a/src/bonsai/bonsai/bim/module/model/opening.py +++ b/src/bonsai/bonsai/bim/module/model/opening.py @@ -151,23 +151,23 @@ class FilledOpeningGenerator: existing_opening_occurrence, "Model", "Body", "MODEL_VIEW" ) assert representation - + # Check if mapped representation - preserve it - if (representation.RepresentationType == 'MappedRepresentation' and - len(representation.Items) == 1 and - representation.Items[0].is_a("IfcMappedItem")): + if ( + representation.RepresentationType == "MappedRepresentation" + and len(representation.Items) == 1 + and representation.Items[0].is_a("IfcMappedItem") + ): source_rep = representation.Items[0].MappingSource.MappedRepresentation representation = ifcopenshell.util.element.copy_deep( - tool.Ifc.get(), - source_rep, - exclude=["IfcGeometricRepresentationContext"] + tool.Ifc.get(), source_rep, exclude=["IfcGeometricRepresentationContext"] ) else: representation = ifcopenshell.util.representation.resolve_representation(representation) else: # Check for library template before generating from filling template_rep = self.get_opening_template_from_type(filling) - + if template_rep: representation = template_rep else: @@ -222,68 +222,68 @@ class FilledOpeningGenerator: voided_element = opening.VoidsElements[0].RelatingBuildingElement opening_rep = ifcopenshell.util.representation.get_representation(opening, "Model", "Body", "MODEL_VIEW") - + # ALWAYS preserve the existing opening representation (Tessellation, SweptSolid, etc.) preserved_representation = None if opening_rep: - if (opening_rep.RepresentationType == 'MappedRepresentation' and - len(opening_rep.Items) == 1 and - opening_rep.Items[0].is_a("IfcMappedItem")): + if ( + opening_rep.RepresentationType == "MappedRepresentation" + and len(opening_rep.Items) == 1 + and opening_rep.Items[0].is_a("IfcMappedItem") + ): # For mapped representations, copy the underlying representation preserved_representation = ifcopenshell.util.element.copy_deep( tool.Ifc.get(), opening_rep.Items[0].MappingSource.MappedRepresentation, - exclude=["IfcGeometricRepresentationContext"] + exclude=["IfcGeometricRepresentationContext"], ) else: # For direct representations (non-mapped), copy them too preserved_representation = ifcopenshell.util.element.copy_deep( - tool.Ifc.get(), - opening_rep, - exclude=["IfcGeometricRepresentationContext"] + tool.Ifc.get(), opening_rep, exclude=["IfcGeometricRepresentationContext"] ) - + ifcopenshell.api.geometry.unassign_representation(tool.Ifc.get(), product=opening, representation=opening_rep) ifcopenshell.api.geometry.remove_representation(tool.Ifc.get(), representation=opening_rep) existing_opening_occurrence = self.get_existing_opening_occurrence_if_any(filling) - + # Priority order for choosing representation: # 1. Existing occurrence with Tessellation (best quality) # 2. Library template with Tessellation # 3. Preserved representation from old opening (maintain user's work) # 4. Generate from filling (last resort) - + representation_to_use = None - + if existing_opening_occurrence: representation = ifcopenshell.util.representation.get_representation( existing_opening_occurrence, "Model", "Body", "MODEL_VIEW" ) - - if (representation and - representation.RepresentationType == 'MappedRepresentation' and - len(representation.Items) == 1 and - representation.Items[0].is_a("IfcMappedItem")): + + if ( + representation + and representation.RepresentationType == "MappedRepresentation" + and len(representation.Items) == 1 + and representation.Items[0].is_a("IfcMappedItem") + ): source_rep = representation.Items[0].MappingSource.MappedRepresentation # Prefer Tessellation from existing occurrence over preserved representation - if source_rep.RepresentationType == 'Tessellation': + if source_rep.RepresentationType == "Tessellation": representation_to_use = ifcopenshell.util.element.copy_deep( - tool.Ifc.get(), - source_rep, - exclude=["IfcGeometricRepresentationContext"] + tool.Ifc.get(), source_rep, exclude=["IfcGeometricRepresentationContext"] ) else: representation_to_use = ifcopenshell.util.representation.resolve_representation(representation) - + if not representation_to_use: template_rep = self.get_opening_template_from_type(filling) - if template_rep and template_rep.RepresentationType == 'Tessellation': + if template_rep and template_rep.RepresentationType == "Tessellation": representation_to_use = template_rep - + if not representation_to_use and preserved_representation: representation_to_use = preserved_representation - + if not representation_to_use: opening_obj = tool.Ifc.get_object(opening) if opening_obj: @@ -299,7 +299,7 @@ class FilledOpeningGenerator: ifcopenshell.api.geometry.assign_representation( tool.Ifc.get(), product=opening, representation=mapped_representation ) - + # update voided object representation... voided_elements = ifcopenshell.util.element.get_parts(voided_element) or [voided_element] for voided_element in voided_elements: @@ -314,31 +314,31 @@ class FilledOpeningGenerator: representation=representation, ) - def get_opening_template_from_type(self, filling: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: + def get_opening_template_from_type( + self, filling: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: """ Check if the filling's type has a stored opening template from library import. """ element_type = ifcopenshell.util.element.get_type(filling) - + if not element_type: return None - + desc = element_type.Description - + if not desc or "||BonsaiOpeningTemplate:" not in desc: return None - + # Extract template ID marker = desc.split("||BonsaiOpeningTemplate:")[-1] template_id = int(marker.split("||")[0]) - + try: template_rep = tool.Ifc.get().by_id(template_id) # Make a copy so we don't reuse the same representation instance copied = ifcopenshell.util.element.copy_deep( - tool.Ifc.get(), - template_rep, - exclude=["IfcGeometricRepresentationContext"] + tool.Ifc.get(), template_rep, exclude=["IfcGeometricRepresentationContext"] ) return copied except: diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 8f22492132..8bad2e17d4 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -715,11 +715,8 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator): if element.is_a("IfcSurfaceStyle") and not tool.Ifc.get_object_by_identifier(element.id()): ifc_importer.create_style(element) - def store_opening_template_from_library( - self, - element: ifcopenshell.entity_instance, - library_file: ifcopenshell.file + self, element: ifcopenshell.entity_instance, library_file: ifcopenshell.file ) -> None: """ Find an opening representation in the library and copy it to the current file @@ -729,36 +726,36 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator): library_element = library_file.by_guid(element.GlobalId) except: return - + # Find occurrences with openings in the library library_occurrences = ifcopenshell.util.element.get_types(library_element) - + for occurrence in library_occurrences: if not getattr(occurrence, "FillsVoids", None): continue - + library_opening = occurrence.FillsVoids[0].RelatingOpeningElement library_opening_rep = ifcopenshell.util.representation.get_representation( library_opening, "Model", "Body", "MODEL_VIEW" ) - + if not library_opening_rep: continue - + # Check if mapped representation - if (library_opening_rep.RepresentationType == 'MappedRepresentation' and - len(library_opening_rep.Items) == 1 and - library_opening_rep.Items[0].is_a("IfcMappedItem")): - + if ( + library_opening_rep.RepresentationType == "MappedRepresentation" + and len(library_opening_rep.Items) == 1 + and library_opening_rep.Items[0].is_a("IfcMappedItem") + ): + mapped_rep = library_opening_rep.Items[0].MappingSource.MappedRepresentation - + # Store ALL representation types (Tessellation, SweptSolid, etc.) template_rep = ifcopenshell.util.element.copy_deep( - self.file, - mapped_rep, - exclude=["IfcGeometricRepresentationContext"] + self.file, mapped_rep, exclude=["IfcGeometricRepresentationContext"] ) - + # Store reference in type's Description current_desc = element.Description or "" element.Description = f"{current_desc}||BonsaiOpeningTemplate:{template_rep.id()}" diff --git a/src/bonsai/bonsai/bim/module/type/data.py b/src/bonsai/bonsai/bim/module/type/data.py index 119456875a..3d0f506986 100644 --- a/src/bonsai/bonsai/bim/module/type/data.py +++ b/src/bonsai/bonsai/bim/module/type/data.py @@ -101,7 +101,7 @@ class TypeData: element_type = ifcopenshell.util.element.get_type(element) if not element_type: return results - + data = element_type.get_info() if "GlobalId" in data: excluded_keys = ["id", "type"] diff --git a/src/bonsai/bonsai/bim/module/type/operator.py b/src/bonsai/bonsai/bim/module/type/operator.py index b09b9fe5a8..c078d66f67 100644 --- a/src/bonsai/bonsai/bim/module/type/operator.py +++ b/src/bonsai/bonsai/bim/module/type/operator.py @@ -429,18 +429,18 @@ class EnableEditingTypeAttributes(bpy.types.Operator): obj = context.active_object if not obj: return {"CANCELLED"} - + element = tool.Ifc.get_entity(obj) if not element: return {"CANCELLED"} - + element_type = ifcopenshell.util.element.get_type(element) if not element_type: return {"CANCELLED"} - + props = tool.Type.get_object_type_props(obj) props.type_attributes.clear() - + bonsai.bim.helper.import_attributes(element_type, props.type_attributes) props.is_editing_type_attributes = True return {"FINISHED"} @@ -456,7 +456,7 @@ class DisableEditingTypeAttributes(bpy.types.Operator): obj = context.active_object if not obj: return {"CANCELLED"} - + props = tool.Type.get_object_type_props(obj) props.type_attributes.clear() props.property_unset("is_editing_type_attributes") @@ -473,24 +473,24 @@ class EditTypeAttributes(bpy.types.Operator, tool.Ifc.Operator): obj = context.active_object if not obj: return {"CANCELLED"} - + element = tool.Ifc.get_entity(obj) if not element: return {"CANCELLED"} - + element_type = ifcopenshell.util.element.get_type(element) if not element_type: return {"CANCELLED"} - + props = tool.Type.get_object_type_props(obj) attributes = bonsai.bim.helper.export_attributes(props.type_attributes) - + ifcopenshell.api.attribute.edit_attributes(tool.Ifc.get(), product=element_type, attributes=attributes) - + type_obj = tool.Ifc.get_object(element_type) if type_obj: tool.Root.set_object_name(type_obj, element_type) - + bpy.ops.bim.disable_editing_type_attributes() - + return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/type/ui.py b/src/bonsai/bonsai/bim/module/type/ui.py index 24ffdc672e..d596997d5a 100644 --- a/src/bonsai/bonsai/bim/module/type/ui.py +++ b/src/bonsai/bonsai/bim/module/type/ui.py @@ -124,27 +124,28 @@ class BIM_PT_type_attributes(Panel): def draw(self, context): if not TypeData.is_loaded: TypeData.load() - + assert (layout := self.layout) assert (obj := context.active_object) - + if not TypeData.data.get("relating_type"): layout.label(text="No Relating Type", icon="INFO") return - + props = tool.Type.get_object_type_props(obj) - + if props.is_editing_type_attributes: row = layout.row(align=True) row.operator("bim.edit_type_attributes", icon="CHECKMARK", text="Save Attributes") row.operator("bim.disable_editing_type_attributes", icon="CANCEL", text="") - + import bonsai.bim.helper + bonsai.bim.helper.draw_attributes(props.type_attributes, layout) else: row = layout.row() row.operator("bim.enable_editing_type_attributes", icon="GREASEPENCIL", text="Edit") - + for attribute in TypeData.data["relating_type_attributes"]: row = layout.row(align=True) row.label(text=attribute["name"]) diff --git a/src/bonsai/bonsai/core/unit.py b/src/bonsai/bonsai/core/unit.py index a4c52508f1..9594c2b566 100644 --- a/src/bonsai/bonsai/core/unit.py +++ b/src/bonsai/bonsai/core/unit.py @@ -28,9 +28,13 @@ if TYPE_CHECKING: def assign_scene_units(ifc: type[tool.Ifc], unit: type[tool.Unit]) -> None: if unit.is_scene_unit_metric(): - lengthunit = ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix=unit.get_scene_unit_si_prefix("LENGTHUNIT")) + lengthunit = ifc.run( + "unit.add_si_unit", unit_type="LENGTHUNIT", prefix=unit.get_scene_unit_si_prefix("LENGTHUNIT") + ) areaunit = ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix=unit.get_scene_unit_si_prefix("AREAUNIT")) - volumeunit = ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=unit.get_scene_unit_si_prefix("VOLUMEUNIT")) + volumeunit = ifc.run( + "unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=unit.get_scene_unit_si_prefix("VOLUMEUNIT") + ) planeangleunit = ifc.run("unit.add_conversion_based_unit", name="degree") units = [lengthunit, areaunit, volumeunit, planeangleunit] diff --git a/src/bonsai/bonsai/tool/root.py b/src/bonsai/bonsai/tool/root.py index 109af267d6..42bd558bd1 100644 --- a/src/bonsai/bonsai/tool/root.py +++ b/src/bonsai/bonsai/tool/root.py @@ -91,20 +91,21 @@ class Root(bonsai.core.tool.Root): elif dest.is_a("IfcTypeProduct"): if not source.RepresentationMaps: return copied_entities - + # Copy representation maps while preserving mapped representation structures new_maps = [] for i, rep_map in enumerate(source.RepresentationMaps): source_rep = rep_map.MappedRepresentation - # Copy the map itself new_map = ifcopenshell.util.element.copy(tool.Ifc.get(), rep_map) - + # Handle the mapped representation - preserve mapping structure if present - if (source_rep.RepresentationType == 'MappedRepresentation' and - len(source_rep.Items) == 1 and - source_rep.Items[0].is_a("IfcMappedItem")): + if ( + source_rep.RepresentationType == "MappedRepresentation" + and len(source_rep.Items) == 1 + and source_rep.Items[0].is_a("IfcMappedItem") + ): # This is a mapped representation - preserve the structure new_rep = ifcopenshell.util.element.copy(tool.Ifc.get(), source_rep) new_rep.Items = [ifcopenshell.util.element.copy(tool.Ifc.get(), item) for item in source_rep.Items] @@ -118,9 +119,9 @@ class Root(bonsai.core.tool.Root): exclude_callback=exclude_callback, copied_entities=copied_entities, ) - + new_maps.append(new_map) - + dest.RepresentationMaps = new_maps return copied_entities diff --git a/src/bonsai/test/core/test_unit.py b/src/bonsai/test/core/test_unit.py index 2bbd783cc3..a7d73c755f 100644 --- a/src/bonsai/test/core/test_unit.py +++ b/src/bonsai/test/core/test_unit.py @@ -93,7 +93,6 @@ class TestAssignSceneUnits: ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called() subject.assign_scene_units(ifc, unit) - def test_creating_metric_units_with_conversion_based_mass_and_time(self, ifc, unit): unit.is_scene_unit_metric().should_be_called().will_return(True) unit.get_scene_unit_si_prefix("LENGTHUNIT").should_be_called().will_return("MILLI") diff --git a/src/bonsai/test/tool/test_drawing.py b/src/bonsai/test/tool/test_drawing.py index 28f95614f8..960e283084 100644 --- a/src/bonsai/test/tool/test_drawing.py +++ b/src/bonsai/test/tool/test_drawing.py @@ -928,23 +928,24 @@ class TestAddReferenceImage(NewFile): uv_node = material_nodes["Texture Coordinate"] assert len(uv_node.outputs["Generated"].links[:]) == 1 + class TestAddReference(NewFile): def test_add_single_reference(self): """Test adding a single reference file (backward compatibility)""" bpy.ops.bim.create_project() ifc_path = Path("test/files/temp/test.ifc").absolute() bpy.ops.bim.save_project(filepath=str(ifc_path), should_save_as=True) - + # Create a temporary SVG file svg_path = Path("test/files/temp/reference.svg").absolute() svg_path.parent.mkdir(parents=True, exist_ok=True) with open(svg_path, "w") as f: f.write('') - + try: # Add single reference bpy.ops.bim.add_reference(filepath=str(svg_path)) - + # Verify reference was added ifc = tool.Ifc.get() references = [doc for doc in ifc.by_type("IfcDocumentInformation") if doc.Scope == "REFERENCE"] @@ -954,24 +955,24 @@ class TestAddReference(NewFile): # Cleanup if svg_path.exists(): svg_path.unlink() - + def test_add_multiple_references(self): """Test adding multiple reference files at once""" bpy.ops.bim.create_project() ifc_path = Path("test/files/temp/test.ifc").absolute() bpy.ops.bim.save_project(filepath=str(ifc_path), should_save_as=True) - + # Create temporary SVG files temp_dir = Path("test/files/temp").absolute() temp_dir.mkdir(parents=True, exist_ok=True) - + svg_files = [] for i in range(3): svg_path = temp_dir / f"reference_{i}.svg" with open(svg_path, "w") as f: f.write('') svg_files.append(svg_path) - + try: # Test by directly calling core.add_document multiple times # (simulating what the operator does with multiple files) @@ -979,12 +980,13 @@ class TestAddReference(NewFile): for svg_file in svg_files: uri = tool.Ifc.get_uri(str(svg_file), use_relative_path=True) from bonsai.bim import core + core.drawing.add_document(tool.Ifc, tool.Drawing, "REFERENCE", uri=uri) - + # Verify all references were added references = [doc for doc in ifc.by_type("IfcDocumentInformation") if doc.Scope == "REFERENCE"] assert len(references) == 3 - + reference_names = {ref.Name for ref in references} expected_names = {f"reference_{i}" for i in range(3)} assert reference_names == expected_names