mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user