mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 11:18:18 +00:00
ifcpatch - input and file arguments #2751
documented when input is currently required
This commit is contained in:
@@ -27,13 +27,22 @@ import inspect
|
|||||||
import collections
|
import collections
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.util
|
import importlib.util
|
||||||
from typing import Union, Iterable, Optional, Any
|
from typing import Union, Iterable, Optional, Any, TypedDict, Literal
|
||||||
|
from typing_extensions import NotRequired
|
||||||
|
|
||||||
|
|
||||||
__version__ = version = "0.0.0"
|
__version__ = version = "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
def execute(args: dict) -> Union[ifcopenshell.file, str]:
|
class ArgumentsDict(TypedDict):
|
||||||
|
recipe: str
|
||||||
|
file: NotRequired[ifcopenshell.file]
|
||||||
|
input: NotRequired[str]
|
||||||
|
log: NotRequired[str]
|
||||||
|
arguments: NotRequired[list]
|
||||||
|
|
||||||
|
|
||||||
|
def execute(args: ArgumentsDict) -> Union[ifcopenshell.file, str]:
|
||||||
"""Execute a patch recipe
|
"""Execute a patch recipe
|
||||||
|
|
||||||
The details of how the patch recipe is executed depends on the definition of
|
The details of how the patch recipe is executed depends on the definition of
|
||||||
@@ -43,10 +52,13 @@ def execute(args: dict) -> Union[ifcopenshell.file, str]:
|
|||||||
:param args: A dictionary of arguments, corresponding to the parameters
|
:param args: A dictionary of arguments, corresponding to the parameters
|
||||||
listed subsequent to this in this docstring.
|
listed subsequent to this in this docstring.
|
||||||
:type args: dict
|
:type args: dict
|
||||||
:param input: A filepath to the incoming IFC file.
|
|
||||||
:type input: str
|
|
||||||
:param file: An IFC model to apply the patch recipe to.
|
:param file: An IFC model to apply the patch recipe to.
|
||||||
:type file: ifcopenshell.file.file
|
Required for most recipes except the ones that require `input`.
|
||||||
|
:type file: ifcopenshell.file, optional
|
||||||
|
:param input: A filepath to the incoming IFC file.
|
||||||
|
Required/supported only for some recipes, see specific recipes descriptions,
|
||||||
|
in other cases will be ignored.
|
||||||
|
:type input: str, optional
|
||||||
:param recipe: The name of the recipe. This is the same as the filename of
|
:param recipe: The name of the recipe. This is the same as the filename of
|
||||||
the recipe. E.g. "ExtractElements".
|
the recipe. E.g. "ExtractElements".
|
||||||
:type recipe: str
|
:type recipe: str
|
||||||
@@ -58,7 +70,6 @@ def execute(args: dict) -> Union[ifcopenshell.file, str]:
|
|||||||
:type arguments: list
|
:type arguments: list
|
||||||
:return: The result of the patch. This is typically a patched model, either
|
:return: The result of the patch. This is typically a patched model, either
|
||||||
as an object or as a string.
|
as an object or as a string.
|
||||||
:rtype: ifcopenshell.file.file,str
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -77,16 +88,32 @@ def execute(args: dict) -> Union[ifcopenshell.file, str]:
|
|||||||
logger = logging.getLogger("IFCPatch")
|
logger = logging.getLogger("IFCPatch")
|
||||||
recipes = getattr(__import__("ifcpatch.recipes.{}".format(args["recipe"])), "recipes")
|
recipes = getattr(__import__("ifcpatch.recipes.{}".format(args["recipe"])), "recipes")
|
||||||
recipe = getattr(recipes, args["recipe"])
|
recipe = getattr(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", [])
|
arguments = args.get("arguments", [])
|
||||||
if recipe.Patcher.__init__.__doc__ is not None:
|
if recipe.Patcher.__init__.__doc__ is not None:
|
||||||
patcher = recipe.Patcher(args.get("input"), args["file"], logger, *arguments)
|
patcher = recipe.Patcher(args.get("input"), args.get("file"), logger, *arguments)
|
||||||
else:
|
else:
|
||||||
patcher = recipe.Patcher(args.get("input"), args["file"], logger, arguments)
|
patcher = recipe.Patcher(args.get("input"), args.get("file"), logger, arguments)
|
||||||
patcher.patch()
|
patcher.patch()
|
||||||
output = getattr(patcher, "file_patched", patcher.file)
|
output = getattr(patcher, "file_patched", patcher.file)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def get_patch_input_argument_use(recipe: str) -> Literal["REQUIRED", "SUPPORTED", "IGNORED"]:
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
recipe_module = importlib.import_module(f"ifcpatch.recipes.{recipe}")
|
||||||
|
return getattr(recipe_module.Patcher, "input_argument", "IGNORED")
|
||||||
|
|
||||||
|
|
||||||
def write(output: Union[ifcopenshell.file, str], filepath: str) -> None:
|
def write(output: Union[ifcopenshell.file, str], filepath: str) -> None:
|
||||||
"""Write the output of an IFC patch to a file
|
"""Write the output of an IFC patch to a file
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,14 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
|
|
||||||
class Patcher:
|
class Patcher:
|
||||||
def __init__(self, src, file, logger):
|
input_argument = "REQUIRED"
|
||||||
|
|
||||||
|
def __init__(self, src: str, file: None, logger: logging.Logger):
|
||||||
"""Allow ArchiCAD IFC spaces to open as Revit rooms
|
"""Allow ArchiCAD IFC spaces to open as Revit rooms
|
||||||
|
|
||||||
The underlying problem is that Revit does not bring in IFC spaces as
|
The underlying problem is that Revit does not bring in IFC spaces as
|
||||||
@@ -46,17 +51,18 @@ class Patcher:
|
|||||||
requires you to run it using Blender, as the geometric modification
|
requires you to run it using Blender, as the geometric modification
|
||||||
uses the Blender geometry engine.
|
uses the Blender geometry engine.
|
||||||
|
|
||||||
|
`input` argument is required for this recipe, `file` argument is ignored.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
ifcpatch.execute({"input": "input.ifc", "recipe": "FixArchiCADToRevitSpaces", "arguments": []})
|
||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixArchiCADToRevitSpaces", "arguments": []})
|
|
||||||
"""
|
"""
|
||||||
self.src = src
|
self.src = src
|
||||||
self.file = file
|
self.file = file
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
def patch(self):
|
def patch(self) -> None:
|
||||||
import bpy
|
import bpy
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -70,10 +76,11 @@ class Patcher:
|
|||||||
|
|
||||||
bpy.ops.bim.load_project(filepath=self.src)
|
bpy.ops.bim.load_project(filepath=self.src)
|
||||||
|
|
||||||
def recalculate_origin(wall):
|
def recalculate_origin(wall: bpy.types.Object) -> None:
|
||||||
new_origin = wall.matrix_world @ Vector(wall.bound_box[0])
|
new_origin = wall.matrix_world @ Vector(wall.bound_box[0])
|
||||||
if (wall.matrix_world.translation - new_origin).length < 0.001:
|
if (wall.matrix_world.translation - new_origin).length < 0.001:
|
||||||
return
|
return
|
||||||
|
assert isinstance(wall.data, bpy.types.Mesh)
|
||||||
wall.data.transform(
|
wall.data.transform(
|
||||||
Matrix.Translation(
|
Matrix.Translation(
|
||||||
(wall.matrix_world.inverted().to_quaternion() @ (wall.matrix_world.translation - new_origin))
|
(wall.matrix_world.inverted().to_quaternion() @ (wall.matrix_world.translation - new_origin))
|
||||||
|
|||||||
@@ -17,8 +17,14 @@
|
|||||||
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class Patcher:
|
class Patcher:
|
||||||
def __init__(self, src, file, logger, is_solid=True):
|
input_argument = "REQUIRED"
|
||||||
|
|
||||||
|
def __init__(self, src: str, file: None, logger: logging.Logger, is_solid: bool = True):
|
||||||
"""Fix missing or spot-coordinate bugged TINs loading in Revit
|
"""Fix missing or spot-coordinate bugged TINs loading in Revit
|
||||||
|
|
||||||
TINs exported from 12D or Civil 3D may contain dense or highly obtuse
|
TINs exported from 12D or Civil 3D may contain dense or highly obtuse
|
||||||
@@ -56,18 +62,20 @@ class Patcher:
|
|||||||
from civil software. It also requires you to run it using Blender, as
|
from civil software. It also requires you to run it using Blender, as
|
||||||
the geometric modification uses the Blender geometry engine.
|
the geometric modification uses the Blender geometry engine.
|
||||||
|
|
||||||
|
`input` argument is required for this recipe, `file` argument is ignored.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixRevitTINs", "arguments": []})
|
ifcpatch.execute({"input": "input.ifc", "recipe": "FixRevitTINs", "arguments": []})
|
||||||
"""
|
"""
|
||||||
self.src = src
|
self.src = src
|
||||||
self.file = file
|
self.file = file
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.is_solid = is_solid
|
self.is_solid = is_solid
|
||||||
|
|
||||||
def patch(self):
|
def patch(self) -> None:
|
||||||
import bpy
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ from typing import Union
|
|||||||
|
|
||||||
|
|
||||||
class Patcher:
|
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, src: str, file: ifcopenshell.file, logger: logging.Logger, output_dir: Union[str, None] = None):
|
||||||
"""Split an IFC model into multiple models based on building storey
|
"""Split an IFC model into multiple models based on building storey
|
||||||
|
|
||||||
@@ -31,6 +33,9 @@ class Patcher:
|
|||||||
format of {i}-{name}.ifc, where {i} is an ascending number starting from
|
format of {i}-{name}.ifc, where {i} is an ascending number starting from
|
||||||
0 and {name} is the name of the storey.
|
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.
|
:param output_dir: Specifies an output directory where the new IFC models will be saved.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|||||||
Reference in New Issue
Block a user