From cac3d7f6997b40a97dc027b36c56f4e3980ba984 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 10 Jan 2025 16:59:45 +1100 Subject: [PATCH] Fix #5939. Fix #2751. IfcPatch no longer needs the (rarely used) input src arg by default. --- .../bonsai/bim/module/patch/operator.py | 6 ----- src/bonsai/bonsai/tool/patch.py | 6 ++--- src/ifcpatch/ifcpatch/__init__.py | 18 ++------------- .../ifcpatch/recipes/ConvertLengthUnit.py | 2 -- .../recipes/ConvertNestToAggregate.py | 3 +-- .../recipes/ConvertPropertiesToQuantities.py | 3 +-- .../recipes/DowngradeIndexedPolyCurve.py | 3 +-- .../ifcpatch/recipes/ExtractElements.py | 3 +-- .../recipes/ExtractPropertiesToSQLite.py | 2 -- .../recipes/FixArchiCADToRevitDoorSwings.py | 3 +-- .../recipes/FixArchiCADToRevitSpaces.py | 15 ++++++++----- .../FixRevitClassificationCodeTypes.py | 3 +-- src/ifcpatch/ifcpatch/recipes/FixRevitTINs.py | 15 ++++++++----- src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py | 2 -- .../ifcpatch/recipes/MergeDuplicateTypes.py | 3 +-- .../ifcpatch/recipes/MergeProjects.py | 5 +---- src/ifcpatch/ifcpatch/recipes/Migrate.py | 2 -- .../recipes/OffsetObjectPlacements.py | 2 -- .../recipes/OffsetStoreyElevations.py | 3 +-- src/ifcpatch/ifcpatch/recipes/Optimise.py | 3 +-- src/ifcpatch/ifcpatch/recipes/PurgeData.py | 3 +-- .../recipes/RecycleNonRootedElements.py | 3 +-- .../ifcpatch/recipes/RegenerateGlobalIds.py | 3 +-- .../RemoveRevitUniformatClassification.py | 3 +-- .../recipes/RemoveSiteRepresentation.py | 3 +-- .../recipes/ResetAbsoluteCoordinates.py | 2 -- .../recipes/ResetSpatialElementLocations.py | 3 +-- .../ifcpatch/recipes/SetFalseOrigin.py | 5 +---- .../ifcpatch/recipes/SetRefElevation.py | 3 +-- .../recipes/SetWorldCoordinateSystem.py | 2 -- .../ifcpatch/recipes/SplitByBuildingStorey.py | 22 +++++-------------- .../ifcpatch/recipes/TessellateElements.py | 2 -- src/ifcpatch/ifcpatch/recipes/UnsharePsets.py | 21 ++++++++++-------- 33 files changed, 58 insertions(+), 119 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/patch/operator.py b/src/bonsai/bonsai/bim/module/patch/operator.py index fe2b021c1d..0a1f4049ca 100644 --- a/src/bonsai/bonsai/bim/module/patch/operator.py +++ b/src/bonsai/bonsai/bim/module/patch/operator.py @@ -98,12 +98,6 @@ class ExecuteIfcPatch(bpy.types.Operator): if props.should_load_from_memory and tool.Ifc.get(): args["file"] = tool.Ifc.get() - if ifcpatch.get_patch_input_argument_use(recipe_name) == "REQUIRED": - self.report( - {"ERROR"}, - f"The recipe '{recipe_name}' is not currently supported if file is loaded from memory.", - ) - return {"CANCELLED"} else: args["input"] = cast(str, props.ifc_patch_input) args["file"] = cast(ifcopenshell.file, ifcopenshell.open(props.ifc_patch_input)) diff --git a/src/bonsai/bonsai/tool/patch.py b/src/bonsai/bonsai/tool/patch.py index a178f99e2b..356a5001de 100644 --- a/src/bonsai/bonsai/tool/patch.py +++ b/src/bonsai/bonsai/tool/patch.py @@ -33,10 +33,8 @@ class Patch(bonsai.core.tool.Patch): @classmethod def is_filepath_argument(cls, recipe: str, arg_name: str) -> bool: - # TODO: Temporary hack to identify filepath arguments. - # Should mark them as such in the patches documentation - # and process it later. - return recipe == "SplitByBuildingStorey" and arg_name == "output_dir" + # There is probably a more explicit way to do this + return "filepath" in arg_name @classmethod def does_patch_has_output(cls, recipe: str) -> bool: diff --git a/src/ifcpatch/ifcpatch/__init__.py b/src/ifcpatch/ifcpatch/__init__.py index 907ab669fb..3112483785 100644 --- a/src/ifcpatch/ifcpatch/__init__.py +++ b/src/ifcpatch/ifcpatch/__init__.py @@ -95,30 +95,16 @@ def execute(args: ArgumentsDict) -> Union[ifcopenshell.file, str]: else: recipe = importlib.import_module(f"ifcpatch.recipes.{args['recipe']}") - # Ensure file or input is provided. - input_argument = get_patch_input_argument_use(args["recipe"]) - if input_argument == "REQUIRED": - if not args.get("input"): - raise ValueError(f"Recipe {args['recipe']} is requiring 'input' argument to be provided.") - elif "file" not in args: # SUPPORTED, IGNORED. - raise ValueError(f"Recipe {args['recipe']} is requiring 'file' argument to be provided.") - arguments = args.get("arguments", None) or [] if recipe.Patcher.__init__.__doc__ is not None: - patcher = recipe.Patcher(args.get("input"), args.get("file"), logger, *arguments) + patcher = recipe.Patcher(args.get("file"), logger, *arguments) else: - patcher = recipe.Patcher(args.get("input"), args.get("file"), logger, arguments) + patcher = recipe.Patcher(args.get("file"), logger, arguments) patcher.patch() output = getattr(patcher, "file_patched", patcher.file) return output -def get_patch_input_argument_use(recipe: str) -> Literal["REQUIRED", "SUPPORTED", "IGNORED"]: - # try out of tree and subpackage imports - recipe_module = sys.modules.get(f"ifcpatch.recipes.{recipe}") or sys.modules.get(recipe) - return getattr(recipe_module.Patcher, "input_argument", "IGNORED") - - def write(output: Union[ifcopenshell.file, str], filepath: str) -> None: """Write the output of an IFC patch to a file diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py index c555a2035e..297cdfbda6 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py @@ -53,7 +53,6 @@ LengthUnit = typing.Literal[ class Patcher: def __init__( self, - src: str, file: ifcopenshell.file, logger: Logger, unit: LengthUnit = "METER", @@ -76,7 +75,6 @@ class Patcher: # Convert to feet model = ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertLengthUnit", "arguments": ["FOOT"]}) """ - self.src = src self.file = file self.logger = logger self.unit = unit diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertNestToAggregate.py b/src/ifcpatch/ifcpatch/recipes/ConvertNestToAggregate.py index d9df05666d..8471ed28f3 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertNestToAggregate.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertNestToAggregate.py @@ -21,7 +21,7 @@ import ifcopenshell.guid class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Convert nesting relationships to aggregate relationships Some software like Revit won't load nested children elements because @@ -39,7 +39,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertNestToAggregate", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py b/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py index 48482821e2..969461ecc2 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py @@ -26,7 +26,7 @@ from typing import Union class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, property_name: str, quantity_name: str): + def __init__(self, file: ifcopenshell.file, logger: Logger, property_name: str, quantity_name: str): """Converts a property to a standardised quantity IFC can store arbitrary key value metadata associated with a elements @@ -59,7 +59,6 @@ class Patcher: # "NetSideArea", if that standardised quantity exists. ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertPropertiesToQuantities", "arguments": ["Area", "NetSideArea"]}) """ - self.src = src self.file = file self.logger = logger self.source_property_name = property_name diff --git a/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py b/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py index f9afbe6835..ea169f2967 100644 --- a/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py +++ b/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py @@ -21,7 +21,7 @@ import ifcopenshell.util.element class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Downgrade indexed polycurves to simple polylines Low quality IFC viewers like Navisworks do not support various IFC4 @@ -39,7 +39,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "DowngradeIndexedPolyCurve", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/ExtractElements.py b/src/ifcpatch/ifcpatch/recipes/ExtractElements.py index 3947dd919d..e49f1b0af4 100644 --- a/src/ifcpatch/ifcpatch/recipes/ExtractElements.py +++ b/src/ifcpatch/ifcpatch/recipes/ExtractElements.py @@ -25,7 +25,7 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, query: str = "IfcWall"): + def __init__(self, file: ifcopenshell.file, logger: Logger, query: str = "IfcWall"): """Extract certain elements into a new model Extract a subset of elements from an existing IFC data set and save it @@ -48,7 +48,6 @@ class Patcher: # Extract all walls and slabs ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ExtractElements", "arguments": ["IfcWall, IfcSlab"]}) """ - self.src = src self.file = file self.logger = logger self.query = query diff --git a/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py index f9fdc9727a..226052ae19 100644 --- a/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py +++ b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py @@ -34,7 +34,6 @@ except: class Patcher: def __init__( self, - src, file, logger, ): @@ -50,7 +49,6 @@ class Patcher: result = ifcpatch.execute({"input": fn, "file": model, "recipe": "ExtractPropertiesToSQLite"}) ifcpatch.write(result, "output.sqlite") """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py index b087b4e6b8..54e3e32fec 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py +++ b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py @@ -27,7 +27,7 @@ import ifcopenshell.util.element class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Fix missing door swings in Revit when viewing ArchiCAD IFCs ArchiCAD has the ability to store 2D data with objects like doors for @@ -56,7 +56,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixArchiCADToRevitDoorSwings", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitSpaces.py b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitSpaces.py index 49b6507a7e..84941d2bd6 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitSpaces.py +++ b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitSpaces.py @@ -21,9 +21,7 @@ import ifcopenshell class Patcher: - input_argument = "REQUIRED" - - def __init__(self, src: str, file: None, logger: logging.Logger): + def __init__(self, file: None, logger: logging.Logger, filepath: str): """Allow ArchiCAD IFC spaces to open as Revit rooms The underlying problem is that Revit does not bring in IFC spaces as @@ -51,16 +49,21 @@ class Patcher: requires you to run it using Blender, as the geometric modification uses the Blender geometry engine. - `input` argument is required for this recipe, `file` argument is ignored. + `filepath` argument is required for this recipe, `file` argument is + ignored. + + :param filepath: The filepath of the IFC model. This is required to + load into Bonsai. + :filter_glob filepath: *.ifc;*.ifczip;*.ifcxml Example: .. code:: python ifcpatch.execute({"input": "input.ifc", "recipe": "FixArchiCADToRevitSpaces", "arguments": []}) """ - self.src = src self.file = file self.logger = logger + self.filepath = filepath def patch(self) -> None: import bpy @@ -74,7 +77,7 @@ class Patcher: bpy.data.batch_remove(bpy.data.objects) bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True) - bpy.ops.bim.load_project(filepath=self.src) + bpy.ops.bim.load_project(filepath=self.filepath) def recalculate_origin(wall: bpy.types.Object) -> None: new_origin = wall.matrix_world @ Vector(wall.bound_box[0]) diff --git a/src/ifcpatch/ifcpatch/recipes/FixRevitClassificationCodeTypes.py b/src/ifcpatch/ifcpatch/recipes/FixRevitClassificationCodeTypes.py index 21a613c5f0..1ec5b331de 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixRevitClassificationCodeTypes.py +++ b/src/ifcpatch/ifcpatch/recipes/FixRevitClassificationCodeTypes.py @@ -21,7 +21,7 @@ import ifcopenshell.util.element class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Reassigns occurrence classifications to types Revit has a bug (see https://github.com/Autodesk/revit-ifc/issues/691) @@ -35,7 +35,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixRevitClassificationCodeTypes"}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/FixRevitTINs.py b/src/ifcpatch/ifcpatch/recipes/FixRevitTINs.py index 60a08dd876..07184d1cb2 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixRevitTINs.py +++ b/src/ifcpatch/ifcpatch/recipes/FixRevitTINs.py @@ -22,9 +22,7 @@ import logging class Patcher: - input_argument = "REQUIRED" - - def __init__(self, src: str, file: None, logger: logging.Logger, is_solid: bool = True): + def __init__(self, file: None, logger: logging.Logger, filepath: str, is_solid: bool = True): """Fix missing or spot-coordinate bugged TINs loading in Revit TINs exported from 12D or Civil 3D may contain dense or highly obtuse @@ -62,7 +60,12 @@ class Patcher: from civil software. It also requires you to run it using Blender, as the geometric modification uses the Blender geometry engine. - `input` argument is required for this recipe, `file` argument is ignored. + `filepath` argument is required for this recipe, `file` argument is + ignored. + + :param filepath: The filepath of the IFC model. This is required to + load into Bonsai. + :filter_glob filepath: *.ifc;*.ifczip;*.ifcxml Example: @@ -70,8 +73,8 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "recipe": "FixRevitTINs", "arguments": []}) """ - self.src = src self.file = file + self.filepath = filepath self.logger = logger self.is_solid = is_solid @@ -82,7 +85,7 @@ class Patcher: from math import degrees bpy.context.scene.BIMProjectProperties.should_use_native_meshes = True - bpy.ops.bim.load_project(filepath=self.src) + bpy.ops.bim.load_project(filepath=self.filepath) old_history_size = tool.Ifc.get().history_size old_undo_steps = bpy.context.preferences.edit.undo_steps diff --git a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py index 3114d8620a..f382bc6a18 100644 --- a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py +++ b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py @@ -54,7 +54,6 @@ except: class Patcher: def __init__( self, - src, file, logger, sql_type: SQLTypes = "SQLite", @@ -98,7 +97,6 @@ class Patcher: {"input": "input.ifc", "file": model, "recipe": "Ifc2Sql", "arguments": ["sqlite"]} ) """ - self.src = src self.file = file self.logger = logger self.sql_type = sql_type.lower() diff --git a/src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.py b/src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.py index c26fa918c4..6b40b26fbc 100644 --- a/src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.py +++ b/src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.py @@ -23,7 +23,7 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, attribute: str = "Tag"): + def __init__(self, file: ifcopenshell.file, logger: Logger, attribute: str = "Tag"): """Merge duplicate element types via the Tag or another attribute Revit is notorious for creating many duplicate element types. Element @@ -60,7 +60,6 @@ class Patcher: # Explicitly say we want to merge based on the Name attribute ifcpatch.execute({"file": model, "recipe": "MergeDuplicateTypes", "arguments": ["Name"]}) """ - self.src = src self.file = file self.logger = logger self.attribute = attribute diff --git a/src/ifcpatch/ifcpatch/recipes/MergeProjects.py b/src/ifcpatch/ifcpatch/recipes/MergeProjects.py index 7581f7259a..4e06b01274 100644 --- a/src/ifcpatch/ifcpatch/recipes/MergeProjects.py +++ b/src/ifcpatch/ifcpatch/recipes/MergeProjects.py @@ -27,9 +27,7 @@ from logging import Logger class Patcher: - def __init__( - self, src: str, file: ifcopenshell.file, logger: Logger, filepaths: list[Union[str, ifcopenshell.file]] - ): + def __init__(self, file: ifcopenshell.file, logger: Logger, filepaths: list[Union[str, ifcopenshell.file]]): """Merge two or more IFC models into one Note that other than combining the two (or more) IfcProject elements into @@ -50,7 +48,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "MergeProjects", "arguments": ["/path/to/model2.ifc"]}) """ - self.src = src self.file = file self.logger = logger self.filepaths = filepaths diff --git a/src/ifcpatch/ifcpatch/recipes/Migrate.py b/src/ifcpatch/ifcpatch/recipes/Migrate.py index 9938ddd754..cf90464678 100644 --- a/src/ifcpatch/ifcpatch/recipes/Migrate.py +++ b/src/ifcpatch/ifcpatch/recipes/Migrate.py @@ -25,7 +25,6 @@ from logging import Logger class Patcher: def __init__( self, - src: str, file: ifcopenshell.file, logger: Logger, schema: ifcopenshell.util.schema.IFC_SCHEMA = "IFC4", @@ -45,7 +44,6 @@ class Patcher: # Upgrade an IFC2X3 model to IFC4 ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "Migrate", "arguments": ["IFC4"]}) """ - self.src = src self.file = file self.logger = logger self.schema = schema diff --git a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py index 55059e5283..765cef7336 100644 --- a/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py +++ b/src/ifcpatch/ifcpatch/recipes/OffsetObjectPlacements.py @@ -26,7 +26,6 @@ import typing class Patcher: def __init__( self, - src, file, logger, x: typing.Union[str, float] = "0", @@ -89,7 +88,6 @@ class Patcher: # Some crazy 3D rotation and offset ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "OffsetObjectPlacements", "arguments": [12.5,5,2,False,90,90,45]}) """ - self.src = src self.file = file self.logger = logger self.x = float(x) diff --git a/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py b/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py index 5611b4a126..4b3efd7c71 100644 --- a/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py +++ b/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py @@ -20,7 +20,7 @@ import typing class Patcher: - def __init__(self, src, file, logger, z: typing.Union[str, float] = "0"): + def __init__(self, file, logger, z: typing.Union[str, float] = "0"): """Offset building storeys by a particular Z value All objects placed relative to the storeys will also be shifted. @@ -35,7 +35,6 @@ class Patcher: # Shift all storeys up by 42 units ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "OffsetStoreyElevations", "arguments": [42]}) """ - self.src = src self.file = file self.logger = logger self.z = float(z) diff --git a/src/ifcpatch/ifcpatch/recipes/Optimise.py b/src/ifcpatch/ifcpatch/recipes/Optimise.py index bca0c12ef7..79699ae2e6 100644 --- a/src/ifcpatch/ifcpatch/recipes/Optimise.py +++ b/src/ifcpatch/ifcpatch/recipes/Optimise.py @@ -21,7 +21,7 @@ import ifcopenshell.util.element class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Optimise the filesize of an IFC model It is possible to non-losslessly optimise the filesize of an IFC model. @@ -46,7 +46,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "Optimise", "arguments": []}) """ - self.src = src self.file = file self.logger = logger self.optimized_file = ifcopenshell.file(schema=self.file.schema) diff --git a/src/ifcpatch/ifcpatch/recipes/PurgeData.py b/src/ifcpatch/ifcpatch/recipes/PurgeData.py index 9f55b47af9..871120840f 100644 --- a/src/ifcpatch/ifcpatch/recipes/PurgeData.py +++ b/src/ifcpatch/ifcpatch/recipes/PurgeData.py @@ -22,7 +22,7 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger): + def __init__(self, file: ifcopenshell.file, logger: Logger): """Purge IFC properties, relationships, and other data In some rare cases (i.e. "resetting" a model or for security purposes) @@ -51,7 +51,6 @@ class Patcher: # Watch the world burn ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "PurgeData", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py b/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py index c94ddfeb30..93ab762778 100644 --- a/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py +++ b/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py @@ -22,7 +22,7 @@ import ifcopenshell.util.element class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: logging.Logger): + def __init__(self, file: ifcopenshell.file, logger: logging.Logger): """Optimise the filesize of an IFC model by reusing non-rooted elements It is possible to non-losslessly optimise the filesize of an IFC model. @@ -46,7 +46,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "RecycleNonRootedElements", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py index 6a00e54e95..dec1a1db10 100644 --- a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py +++ b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py @@ -22,7 +22,7 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, only_duplicates=False): + def __init__(self, file: ifcopenshell.file, logger: Logger, only_duplicates=False): """Regenerate GlobalIds in an IFC model All root elements in an IFC model must be identified by a unique Global @@ -48,7 +48,6 @@ class Patcher: # Regenerate only duplicate GlobalIds ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "RegenerateGlobalIds", "arguments": [True]}) """ - self.src = src self.file = file self.logger = logger self.only_duplicates = only_duplicates diff --git a/src/ifcpatch/ifcpatch/recipes/RemoveRevitUniformatClassification.py b/src/ifcpatch/ifcpatch/recipes/RemoveRevitUniformatClassification.py index 0a73508139..0d61bbd6c0 100644 --- a/src/ifcpatch/ifcpatch/recipes/RemoveRevitUniformatClassification.py +++ b/src/ifcpatch/ifcpatch/recipes/RemoveRevitUniformatClassification.py @@ -18,7 +18,7 @@ class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Removes the built-in Revit Uniformat classification. Revit has a bug (see https://github.com/Autodesk/revit-ifc/issues/486) @@ -31,7 +31,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "RemoveRevitUniformatClassification"}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py b/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py index 39b326a4c9..c5daea9868 100644 --- a/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py +++ b/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py @@ -18,7 +18,7 @@ class Patcher: - def __init__(self, src, file, logger): + def __init__(self, file, logger): """Removes any 3D geometry associated with a site or multiple sites If no sites or no site geometry is present, nothing happens. @@ -29,7 +29,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "RemoveSiteRepresentation", "arguments": []}) """ - self.src = src self.file = file self.logger = logger diff --git a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py index 7f58e39694..ee0b4c1568 100644 --- a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py +++ b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py @@ -26,7 +26,6 @@ from typing import Literal, Optional, Union class Patcher: def __init__( self, - src: str, file: ifcopenshell.file, logger: logging.Logger, mode: Literal[ @@ -99,7 +98,6 @@ class Patcher: # Reset all coordinates with an ordinate larger than 1000 by -500,-200,0 ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [False, 1000, -500,-200,0]}) """ - self.src = src self.file = file self.logger = logger self.mode = mode.lower() diff --git a/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py b/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py index f77516eb4b..aa335320ba 100644 --- a/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py +++ b/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py @@ -18,7 +18,7 @@ class Patcher: - def __init__(self, src, file, logger, ifc_class="IfcSite"): + def __init__(self, file, logger, ifc_class="IfcSite"): """Resets the location of a spatial element to 0,0,0 Another more specialised patch to fix incorrect coordinate usage is to @@ -35,7 +35,6 @@ class Patcher: # All IfcSites will shift back to 0,0,0. ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetSpatialElementLocations", "arguments": ["IfcSite"]}) """ - self.src = src self.file = file self.logger = logger self.ifc_class = ifc_class diff --git a/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py b/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py index 8657890418..73008c8fdf 100644 --- a/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py +++ b/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py @@ -25,7 +25,6 @@ import typing class Patcher: def __init__( self, - src, file, logger, name: str = "EPSG:1234", @@ -59,7 +58,6 @@ class Patcher: # Set the current origin 0,0,0 to correlate to map coordinates 1000,1000,0 and a grid north of 15. ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "SetFalseOrigin", "arguments": ["EPSG:1234", 0, 0, 0, 1000, 1000, 0, 15, 0]}) """ - self.src = src self.file = file self.logger = logger self.name = name @@ -73,7 +71,7 @@ class Patcher: self.rotate_angle = float(rotate_angle) def patch(self): - SetWorldCoordinateSystem.Patcher(self.src, self.file, self.logger, x=0, y=0, z=0, ax=0, ay=0, az=0).patch() + SetWorldCoordinateSystem.Patcher(self.file, self.logger, x=0, y=0, z=0, ax=0, ay=0, az=0).patch() coordinate_operation = { "Eastings": self.e, "Northings": self.n, @@ -90,7 +88,6 @@ class Patcher: self.file, projected_crs={"Name": self.name}, coordinate_operation=coordinate_operation ) OffsetObjectPlacements.Patcher( - self.src, self.file, self.logger, x=-self.x, diff --git a/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py b/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py index cc1a092aff..a39b564cd9 100644 --- a/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py +++ b/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py @@ -20,7 +20,7 @@ import typing class Patcher: - def __init__(self, src, file, logger, elevation: typing.Union[str, float] = "0"): + def __init__(self, file, logger, elevation: typing.Union[str, float] = "0"): """Sets the reference elevation of all IfcSites To completely reference model coordinates, a reference elevation should @@ -41,7 +41,6 @@ class Patcher: # All IfcSites will have their reference elevation set to 42. ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "SetRefElevation", "arguments": [42]}) """ - self.src = src self.file = file self.logger = logger self.elevation = float(elevation) diff --git a/src/ifcpatch/ifcpatch/recipes/SetWorldCoordinateSystem.py b/src/ifcpatch/ifcpatch/recipes/SetWorldCoordinateSystem.py index ff8a4a4043..ba83d9a556 100644 --- a/src/ifcpatch/ifcpatch/recipes/SetWorldCoordinateSystem.py +++ b/src/ifcpatch/ifcpatch/recipes/SetWorldCoordinateSystem.py @@ -24,7 +24,6 @@ import typing class Patcher: def __init__( self, - src, file, logger, x: typing.Union[str, float] = "0", @@ -61,7 +60,6 @@ class Patcher: # Set the world coordinate system back to 0, 0, 0 ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "SetWorldCoordinateSystem", "arguments": [0,0,0,0,0,0]}) """ - self.src = src self.file = file self.logger = logger self.x = float(x) diff --git a/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py b/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py index 82f4e32fc4..a2cee58a08 100644 --- a/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py +++ b/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py @@ -24,18 +24,13 @@ from typing import Union class Patcher: - input_argument = "SUPPORTED" - - def __init__(self, src: str, file: ifcopenshell.file, logger: logging.Logger, output_dir: Union[str, None] = None): + def __init__(self, file: ifcopenshell.file, logger: logging.Logger, output_dir: Union[str, None] = None): """Split an IFC model into multiple models based on building storey The new IFC model names will be named after the storey name in the format of {i}-{name}.ifc, where {i} is an ascending number starting from 0 and {name} is the name of the storey. - `input` argument might be provided to ifcpatch - it will be used load file from disk - (otherwise `file` will be saved to a temporary file). - :param output_dir: Specifies an output directory where the new IFC models will be saved. Example: @@ -44,7 +39,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "SplitByBuildingStorey", "arguments": ["C:/.../output_files"]}) """ - self.src = src self.file = file self.logger = logger self.output_dir = output_dir @@ -60,17 +54,14 @@ class Patcher: output_dir = Path(self.output_dir) output_dir.mkdir(parents=True, exist_ok=True) - temp_file = None - if not self.src: - temp_file = tempfile.NamedTemporaryFile(suffix=".ifc", delete=False) - self.src = temp_file.name - self.file.write(self.src) + temp_file = tempfile.NamedTemporaryFile(suffix=".ifc", delete=False) + self.file.write(temp_file.name) storeys = self.file.by_type("IfcBuildingStorey") for i, storey in enumerate(storeys): filename = f"{i}-{storey.Name}.ifc" dest = filename if output_dir == None else output_dir / filename - copyfile(self.src, dest) + copyfile(temp_file.name, dest) old_ifc: ifcopenshell.file = ifcopenshell.open(dest) new_ifc = ifcopenshell.file(schema=self.file.schema) @@ -98,9 +89,8 @@ class Patcher: new_ifc.remove(element) new_ifc.write(dest) - if temp_file is not None: - temp_file.close() - os.unlink(temp_file.name) + temp_file.close() + os.unlink(temp_file.name) def is_in_storey(self, element: ifcopenshell.entity_instance, storey: ifcopenshell.entity_instance) -> bool: return ( diff --git a/src/ifcpatch/ifcpatch/recipes/TessellateElements.py b/src/ifcpatch/ifcpatch/recipes/TessellateElements.py index 06929cea59..5becec4264 100644 --- a/src/ifcpatch/ifcpatch/recipes/TessellateElements.py +++ b/src/ifcpatch/ifcpatch/recipes/TessellateElements.py @@ -30,7 +30,6 @@ from typing import Union class Patcher: def __init__( self, - src: str, file: ifcopenshell.file, logger: Logger, query: str = "IfcBeam", @@ -58,7 +57,6 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "TessellateElements", "arguments": ["IfcBeam", False]}) """ - self.src = src self.file = file self.logger = logger self.query = query diff --git a/src/ifcpatch/ifcpatch/recipes/UnsharePsets.py b/src/ifcpatch/ifcpatch/recipes/UnsharePsets.py index 7020a86504..fa7f1b083c 100644 --- a/src/ifcpatch/ifcpatch/recipes/UnsharePsets.py +++ b/src/ifcpatch/ifcpatch/recipes/UnsharePsets.py @@ -25,18 +25,22 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, query: str = ""): + def __init__(self, file: ifcopenshell.file, logger: Logger, query: str = ""): """Create independent copies for shared psets in IFC file. - In IFC it's possible that same property set is shared by multiple elements, - so editing it's properties will automatically change their values for all those elements. + In IFC it's possible that same property set is shared by multiple + elements, so editing it's properties will automatically change their + values for all those elements. - Sometimes it's intended but sometimes it's not and it's just the way some other - software exports IFC (e.g. there is a known case when Tekla exports shared psets for all the occurrences). - While it is more optimized way to store data, it may lead to unexpected results when editing properties. + Sometimes it's intended but sometimes it's not and it's just the way + some other software exports IFC (e.g. there is a known case when Tekla + exports shared psets for all the occurrences). While it is more + optimized way to store data, it may lead to unexpected results when + editing properties. - This recipe creates independent copies of all shared psets (may be limited by the query) - and assigns them to the elements, so they can be edited without affecting any other elements. + This recipe creates independent copies of all shared psets (may be + limited by the query) and assigns them to the elements, so they can be + edited without affecting any other elements. :param query: A query to select the subset of IFC elements, optional. If not provided, patch will be applied to all shared property sets in the model. @@ -51,7 +55,6 @@ class Patcher: # Unshare psets on all IfcWalls. ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "UnsharePsets", "arguments": ["IfcWall"]}) """ - self.src = src self.file = file self.logger = logger self.query = query