From 590c2e120cdd31c784ee066e9a76e0b127ad5480 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 5 Feb 2025 17:17:09 +1100 Subject: [PATCH] Fix failing drawing tool tests. Also consolidate relative paths using tool.Ifc.get_uri. The same issue of handling relative paths, doing as_posix, checking drives, etc was solved again and again in slightly different ways. This centralises it. --- .../bonsai/bim/module/drawing/operator.py | 53 +++++-------------- .../bonsai/bim/module/project/operator.py | 13 ++--- .../bonsai/bim/module/style/operator.py | 21 ++------ src/bonsai/bonsai/bim/operator.py | 5 +- src/bonsai/bonsai/tool/ifc.py | 15 ++++-- src/bonsai/bonsai/tool/loader.py | 1 + src/bonsai/test/tool/test_drawing.py | 11 ++-- 7 files changed, 40 insertions(+), 79 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 4c35cf2139..1bfee506a4 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -694,8 +694,8 @@ class CreateDrawing(bpy.types.Operator): invalidated_elements = exporter.sync_all_objects() invalidated_elements += exporter.sync_edited_objects() invalidated_guids = [e.GlobalId for e in invalidated_elements if hasattr(e, "GlobalId")] - cache = IfcStore.get_cache() - [cache.remove(guid) for guid in invalidated_guids] + if cache := IfcStore.get_cache(): + [cache.remove(guid) for guid in invalidated_guids] # If we have already calculated it in the SVG in the past, don't recalculate edited_guids = set() @@ -1752,7 +1752,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator): sheet_builder = sheeter.SheetBuilder() references = sheet_builder.build(sheet) - raster_references = [tool.Ifc.get_relative_uri(r) for r in references["RASTER"]] + raster_references = [tool.Ifc.get_uri(r, use_relative_path=True) for r in references["RASTER"]] # These variables will be made available to the evaluated commands svg = references["SHEET"] @@ -1783,7 +1783,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator): "document.edit_reference", reference=reference, attributes=tool.Drawing.generate_reference_attributes( - reference, Location=tool.Ifc.get_relative_uri(svg), Description="SHEET" + reference, Location=tool.Ifc.get_uri(svg, use_relative_path=True), Description="SHEET" ), ) @@ -1793,7 +1793,9 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator): "document.edit_reference", reference=reference, attributes=tool.Drawing.generate_reference_attributes( - reference, Location=tool.Ifc.get_relative_uri(raster_reference), Description="RASTER" + reference, + Location=tool.Ifc.get_uri(raster_reference, use_relative_path=True), + Description="RASTER", ), ) @@ -2455,21 +2457,8 @@ class AddSchedule(bpy.types.Operator, tool.Ifc.Operator): use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True) def _execute(self, context): - filepath = self.filepath - if self.use_relative_path: - ifc_path = tool.Ifc.get_path() - if os.path.isfile(ifc_path): - ifc_path = os.path.dirname(ifc_path) - - # taking into account different drives on windows - if Path(filepath).drive == Path(ifc_path).drive: - filepath = os.path.relpath(filepath, ifc_path) - core.add_document( - tool.Ifc, - tool.Drawing, - "SCHEDULE", - uri=filepath, - ) + filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path) + core.add_document(tool.Ifc, tool.Drawing, "SCHEDULE", uri=filepath) def invoke(self, context, event): context.window_manager.fileselect_add(self) @@ -2653,21 +2642,8 @@ class AddReference(bpy.types.Operator, tool.Ifc.Operator): use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True) def _execute(self, context): - filepath = self.filepath - if self.use_relative_path: - ifc_path = tool.Ifc.get_path() - if os.path.isfile(ifc_path): - ifc_path = os.path.dirname(ifc_path) - - # taking into account different drives on windows - if Path(filepath).drive == Path(ifc_path).drive: - filepath = os.path.relpath(filepath, ifc_path) - core.add_document( - tool.Ifc, - tool.Drawing, - "REFERENCE", - uri=filepath, - ) + filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path) + core.add_document(tool.Ifc, tool.Drawing, "REFERENCE", uri=filepath) def invoke(self, context, event): context.window_manager.fileselect_add(self) @@ -3301,11 +3277,8 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator): return {"RUNNING_MODAL"} def _execute(self, context): - abs_path = Path(self.filepath) - if self.use_relative_path: - image_filepath = abs_path.relative_to(Path(tool.Ifc.get_path()).parent) - else: - image_filepath = abs_path + abs_path = Path(self.filepath).absolute().resolve() + image_filepath = Path(tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path)) ifc_file = tool.Ifc.get() if self.override_existing_image: diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 81091d466e..32217d9ef2 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -1135,17 +1135,12 @@ class LinkIfc(bpy.types.Operator): self.report({"INFO"}, "Can't link the current .blend file") continue new = context.scene.BIMProjectProperties.links.add() - if self.use_relative_path and (ifc_filepath := tool.Ifc.get_path()): - try: - filepath = filepath.relative_to(Path(ifc_filepath).parent) - except: - pass # Perhaps on another drive or something - # Store link paths as posix for cross-platform. - new.name = filepath.as_posix() - status = bpy.ops.bim.load_link(filepath=filepath.as_posix(), use_cache=self.use_cache) + filepath = tool.Ifc.get_uri(filepath, use_relative_path=self.use_relative_path) + new.name = filepath + status = bpy.ops.bim.load_link(filepath=filepath, use_cache=self.use_cache) if status == {"CANCELLED"}: error_msg = ( - f'Error processing IFC file "{filepath.as_posix()}" ' + f'Error processing IFC file "{filepath}" ' "was critical and blend file either wasn't saved or wasn't updated. " "See logs above in system console for details." ) diff --git a/src/bonsai/bonsai/bim/module/style/operator.py b/src/bonsai/bonsai/bim/module/style/operator.py index ac8da79417..ef7914c69e 100644 --- a/src/bonsai/bonsai/bim/module/style/operator.py +++ b/src/bonsai/bonsai/bim/module/style/operator.py @@ -387,12 +387,7 @@ class BrowseExternalStyle(bpy.types.Operator): bpy.data.materials.remove(db["data_block"]) - if self.use_relative_path: - filepath = os.path.relpath(self.filepath, Path(tool.Ifc.get_path()).parent) - else: - filepath = self.filepath - filepath = Path(filepath).as_posix() - + filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path) attributes = context.scene.BIMStylesProperties.external_style_attributes attributes["Location"].string_value = filepath attributes["Identification"].string_value = f"{self.data_block_type}/{self.data_block}" @@ -565,19 +560,9 @@ class ChooseTextureMapPath(bpy.types.Operator): self.report({"ERROR"}, "Provide a texture map index") return {"CANCELLED"} - abs_path = Path(self.filepath) - if self.use_relative_path: - parent = Path(tool.Ifc.get_path()).parent - if abs_path.is_relative_to(parent): - image_filepath = abs_path.relative_to(parent) - else: - self.report({"INFO"}, "Path is not relative to the .ifc file, it will be saved as absolute.") - image_filepath = abs_path - else: - image_filepath = abs_path - + filepath = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path) texture = context.scene.BIMStylesProperties.textures[self.texture_map_index] - texture.path = image_filepath.as_posix() + texture.path = filepath return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/operator.py b/src/bonsai/bonsai/bim/operator.py index dcea3766a5..5ba1596203 100644 --- a/src/bonsai/bonsai/bim/operator.py +++ b/src/bonsai/bonsai/bim/operator.py @@ -166,10 +166,7 @@ class SelectURIAttribute(bpy.types.Operator): # Do you know a better way? pass if attribute: - filepath = self.filepath - if self.use_relative_path: - filepath = os.path.relpath(filepath, os.path.dirname(tool.Ifc.get_path())) - attribute.string_value = filepath + attribute.string_value = tool.Ifc.get_uri(self.filepath, use_relative_path=self.use_relative_path) return {"FINISHED"} def invoke(self, context, event): diff --git a/src/bonsai/bonsai/tool/ifc.py b/src/bonsai/bonsai/tool/ifc.py index 502903601d..5b3f8bb48a 100644 --- a/src/bonsai/bonsai/tool/ifc.py +++ b/src/bonsai/bonsai/tool/ifc.py @@ -26,6 +26,7 @@ import ifcopenshell.util.schema import bonsai.core.tool import bonsai.bim.handler import bonsai.tool as tool +from pathlib import Path from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE from typing import Optional, Union, Any, final, Literal @@ -247,13 +248,17 @@ class Ifc(bonsai.core.tool.Ifc): return (uri if not uri else os.path.join(ifc_path, uri)).replace("\\", "/") @classmethod - def get_relative_uri(cls, uri: str) -> str: - if not os.path.isabs(uri): - return uri - ifc_path = cls.get_path() + def get_uri(cls, uri: str | Path, use_relative_path: bool = False) -> str: + if not use_relative_path: + return Path(uri).absolute().resolve().as_posix() + uri = Path(uri) + if not os.path.isabs(uri) or not (ifc_path := cls.get_path()): + return uri.as_posix().replace("\\", "/") + if Path(uri).drive != Path(ifc_path).drive: + return uri.as_posix().replace("\\", "/") if os.path.isfile(ifc_path): ifc_path = os.path.dirname(ifc_path) - return os.path.relpath(uri, ifc_path).replace("\\", "/") + return Path(os.path.relpath(uri, ifc_path)).as_posix().replace("\\", "/") @classmethod def unlink( diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 2881d75747..930fa90c72 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -298,6 +298,7 @@ class Loader(bonsai.core.tool.Loader): if is_relative: ifc_path = Path(tool.Ifc.get_path()) image_url = ifc_path.parent / image_url + image_url = image_url.absolute().resolve() if not image_url.exists(): print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.") diff --git a/src/bonsai/test/tool/test_drawing.py b/src/bonsai/test/tool/test_drawing.py index 241588cc3a..d895b4f306 100644 --- a/src/bonsai/test/tool/test_drawing.py +++ b/src/bonsai/test/tool/test_drawing.py @@ -724,8 +724,8 @@ class TestDrawingMaintainingSheetPosition(NewFile): # check drawing position on the sheet drawing_data = self.get_sheet_drawing_data(sheet_path) - assert drawing_data["foreground"] == (155.0, 155.0, 250.0, 250.0) - assert drawing_data["view-title"] == (155.0, 410.0, 50.22, 10.0) + assert drawing_data["foreground"] == (30.0, 30.0, 500.0, 500.0) + assert drawing_data["view-title"] == (30.0, 535.0, 50.22, 10.0) class TestUpdateTextValue(NewFile): @@ -835,6 +835,9 @@ class TestDrawingStyles(NewFile): bpy.ops.bim.add_drawing() ifc = tool.Ifc.get() drawing = ifc.by_type("IfcAnnotation")[0] + bpy.ops.bim.expand_target_view(target_view="PLAN_VIEW") + props = bpy.context.scene.DocProperties + props.active_drawing_index = 2 bpy.ops.bim.activate_drawing(drawing=drawing.id()) self.drawing_styles = bpy.context.scene.DocProperties.drawing_styles @@ -857,7 +860,8 @@ class TestAddReferenceImage(NewFile): def test_run(self): bpy.context.scene.BIMProjectProperties.template_file = "0" bpy.ops.bim.create_project() - ifc_file = tool.Ifc.get() + ifc_path = Path("test/files/temp/test.ifc").absolute() + bpy.ops.bim.save_project(filepath=str(ifc_path), should_save_as=True) filepath = Path("test/files/image.jpg").absolute() bpy.ops.bim.add_reference_image(filepath=str(filepath)) @@ -870,6 +874,7 @@ class TestAddReferenceImage(NewFile): assert material.name == "image" assert material.BIMStyleProperties.ifc_definition_id != 0 + ifc_file = tool.Ifc.get() style = ifc_file.by_id(material.BIMStyleProperties.ifc_definition_id) styled_items = set(tool.Style.get_styled_items(style)) representation_items = set(tool.Geometry.get_active_representation(obj).Items)