diff --git a/src/ifcopenshell-python/docs/ifcpatch.rst b/src/ifcopenshell-python/docs/ifcpatch.rst index 7407c2c3ab..171e2ed063 100644 --- a/src/ifcopenshell-python/docs/ifcpatch.rst +++ b/src/ifcopenshell-python/docs/ifcpatch.rst @@ -57,10 +57,8 @@ Here is a minimal example of how to use IfcDiff as a library: import ifcpatch ifcpatch.execute({ - "input": "input.ifc", - "output": "output.ifc", + "input": ifcopenshell.open("input.ifc"), "recipe": "ExtractElements", - "log": "ifcpatch.log", "arguments": [".IfcWall"], }) diff --git a/src/ifcpatch/ifcpatch/__init__.py b/src/ifcpatch/ifcpatch/__init__.py index 74f38c36dd..7b163f6c42 100644 --- a/src/ifcpatch/ifcpatch/__init__.py +++ b/src/ifcpatch/ifcpatch/__init__.py @@ -18,8 +18,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . -# This can be packaged into one executable with ./make.py - import ifcopenshell import logging import os @@ -29,39 +27,24 @@ import collections import importlib -def execute(args, is_library=None): - logging.basicConfig(filename=args["log"], filemode="a", level=logging.DEBUG) +def execute(args): + if "log" in args: + logging.basicConfig(filename=args["log"], filemode="a", level=logging.DEBUG) logger = logging.getLogger("IFCPatch") - print("# Loading IFC file ...") - ifc_file = ifcopenshell.open(args["input"]) - print("# Loading patch recipe ...") + ifc_file = args["input"] recipes = getattr(__import__("ifcpatch.recipes.{}".format(args["recipe"])), "recipes") recipe = getattr(recipes, args["recipe"]) if recipe.Patcher.__init__.__doc__ is not None: patcher = recipe.Patcher(args["input"], ifc_file, logger, *args["arguments"]) else: patcher = recipe.Patcher(args["input"], ifc_file, logger, args["arguments"]) - print("# Patching ...") patcher.patch() - ifc_file = getattr(patcher, "file_patched", patcher.file) - if is_library is True: - return ifc_file - print("# Writing patched file ...") - if not args["output"]: - args["output"] = args["input"] - if isinstance(ifc_file, str): - with open(args["output"], "w") as text_file: - text_file.write(ifc_file) - else: - ifc_file.write(args["output"]) - print("# All tasks are complete :-)") + return getattr(patcher, "file_patched", patcher.file) def extract_docs( - submodule_name: str, - cls_name: str, - method_name: str="__init__", - boilerplate_args : typing.Iterable[str]=None): + submodule_name: str, cls_name: str, method_name: str = "__init__", boilerplate_args: typing.Iterable[str] = None +): """Extract class docstrings and method arguments :param submodule_name: Submodule from which to extract the class @@ -70,8 +53,8 @@ def extract_docs( :param boilerplate_args: String iterable containing arguments that shall not be parsed """ spec = importlib.util.spec_from_file_location( - submodule_name, - f"{os.path.dirname(inspect.getabsfile(inspect.currentframe()))}/recipes/{submodule_name}.py") + submodule_name, f"{os.path.dirname(inspect.getabsfile(inspect.currentframe()))}/recipes/{submodule_name}.py" + ) submodule = importlib.util.module_from_spec(spec) try: spec.loader.exec_module(submodule) @@ -101,7 +84,7 @@ def _extract_docs(cls, method_name, boilerplate_args): type_hints = typing.get_type_hints(method) for input_name in inputs.keys(): type_hint = type_hints.get(input_name, None) - if type_hint is None: # The argument is not type-hinted. (Or hinted to None ??) + if type_hint is None: # The argument is not type-hinted. (Or hinted to None ??) continue if isinstance(type_hint, typing._UnionGenericAlias): inputs[input_name]["type"] = [t.__name__ for t in typing.get_args(type_hint)] @@ -125,7 +108,7 @@ def _extract_docs(cls, method_name, boilerplate_args): description += line elif i > 2: description += "\n" + line - + docs["description"] = description.strip() docs["inputs"] = inputs return docs diff --git a/src/ifcpatch/ifcpatch/__main__.py b/src/ifcpatch/ifcpatch/__main__.py index 115326b2c0..c95a05ed8f 100644 --- a/src/ifcpatch/ifcpatch/__main__.py +++ b/src/ifcpatch/ifcpatch/__main__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # IfcPatch - IFC patching utiliy -# Copyright (C) 2020, 2021 Dion Moult +# Copyright (C) 2020, 2021, 2022 Dion Moult # # This file is part of IfcPatch. # @@ -20,6 +20,7 @@ import argparse import ifcpatch +import ifcopenshell parser = argparse.ArgumentParser(description="Patches IFC files to fix badly formatted data") parser.add_argument("-i", "--input", type=str, required=True, help="The IFC file to patch") @@ -28,4 +29,20 @@ parser.add_argument("-r", "--recipe", type=str, required=True, help="Name of the parser.add_argument("-l", "--log", type=str, help="Specify a log file", default="ifcpatch.log") parser.add_argument("-a", "--arguments", nargs="+", help="Specify custom arguments to the patch recipe") args = vars(parser.parse_args()) -ifcpatch.execute(args) + +print("# Loading IFC file ...") +args["input"] = ifcopenshell.open(args["input"]) + +print("# Patching ...") +ifc_file = ifcpatch.execute(args) + +print("# Writing patched file ...") +if not args["output"]: + args["output"] = args["input"] +if isinstance(ifc_file, str): + with open(args["output"], "w") as text_file: + text_file.write(ifc_file) +else: + ifc_file.write(args["output"]) + +print("# All tasks are complete :-)") diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py index 11151fdd22..9192e977c7 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -37,13 +36,13 @@ class Patcher: if self.file.schema == "IFC2X3": user = self.file_patched.add(self.file.by_type("IfcProject")[0].OwnerHistory.OwningUser) old_get_user = ifcopenshell.api.owner.settings.get_user - ifcopenshell.api.owner.settings.get_user = lambda ifc : user + ifcopenshell.api.owner.settings.get_user = lambda ifc: user project = ifcopenshell.api.run("root.create_entity", self.file_patched, ifc_class="IfcProject") unit_assignment = ifcopenshell.api.run("unit.assign_unit", self.file_patched, **{"length": unit}) # Is there a better way? for element in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): - element.Precision = 1E-8 + element.Precision = 1e-8 # If we don't add openings first, they don't get converted for element in self.file.by_type("IfcOpeningElement"): diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py b/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py index 102639ef46..36db19f82b 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertPropertiesToQuantities.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # diff --git a/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py b/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py index ac358347e2..ea10b67ce2 100644 --- a/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py +++ b/src/ifcpatch/ifcpatch/recipes/DowngradeIndexedPolyCurve.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # diff --git a/src/ifcpatch/ifcpatch/recipes/Fix12DToRevitTINs.py b/src/ifcpatch/ifcpatch/recipes/Fix12DToRevitTINs.py index f4b88c8b9b..0126ece36a 100644 --- a/src/ifcpatch/ifcpatch/recipes/Fix12DToRevitTINs.py +++ b/src/ifcpatch/ifcpatch/recipes/Fix12DToRevitTINs.py @@ -44,13 +44,13 @@ class Patcher: bm = bmesh.new() bm.from_mesh(data) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.01) - bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method='BEAUTY', ngon_method='BEAUTY') + bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method="BEAUTY", ngon_method="BEAUTY") bm.faces.ensure_lookup_table() for polygon in bm.faces: v1, v2, v3 = [v.co.to_2d() for v in polygon.verts] - d1 = degrees((v2-v1).angle(v3-v1)) - d2 = degrees((v3-v2).angle(v1-v2)) - d3 = degrees((v1-v3).angle(v2-v3)) + d1 = degrees((v2 - v1).angle(v3 - v1)) + d2 = degrees((v3 - v2).angle(v1 - v2)) + d3 = degrees((v1 - v3).angle(v2 - v3)) if d1 < angle_threshold or d2 < angle_threshold or d3 < angle_threshold: bm.faces.remove(polygon) bm.to_mesh(data) diff --git a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py index 8f1c941198..7a509a2862 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py +++ b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py @@ -45,7 +45,7 @@ class Patcher: # # See bug https://github.com/Autodesk/revit-ifc/issues/187 - #for context in self.file.by_type("IfcGeometricRepresentationSubContext"): + # for context in self.file.by_type("IfcGeometricRepresentationSubContext"): # if context.ContextIdentifier == "FootPrint": # context.ContextIdentifier = None # context.ContextType = "Annotation" diff --git a/src/ifcpatch/ifcpatch/recipes/MergeProject.py b/src/ifcpatch/ifcpatch/recipes/MergeProject.py index c413ce013b..819791b635 100644 --- a/src/ifcpatch/ifcpatch/recipes/MergeProject.py +++ b/src/ifcpatch/ifcpatch/recipes/MergeProject.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -20,6 +19,7 @@ import ifcopenshell import ifcopenshell.util.element + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -29,9 +29,9 @@ class Patcher: def patch(self): source = ifcopenshell.open(self.args[0]) - original_project = self.file.by_type('IfcProject')[0] - merged_project = self.file.add(source.by_type('IfcProject')[0]) - for element in source.by_type('IfcRoot'): + original_project = self.file.by_type("IfcProject")[0] + merged_project = self.file.add(source.by_type("IfcProject")[0]) + for element in source.by_type("IfcRoot"): self.file.add(element) for inverse in self.file.get_inverse(merged_project): ifcopenshell.util.element.replace_attribute(inverse, merged_project, original_project) diff --git a/src/ifcpatch/ifcpatch/recipes/Migrate.py b/src/ifcpatch/ifcpatch/recipes/Migrate.py index bc31388e2b..e94652b567 100644 --- a/src/ifcpatch/ifcpatch/recipes/Migrate.py +++ b/src/ifcpatch/ifcpatch/recipes/Migrate.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # diff --git a/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py b/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py index 79a35fb66d..a92389732e 100644 --- a/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py +++ b/src/ifcpatch/ifcpatch/recipes/OffsetStoreyElevations.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -25,11 +25,11 @@ class Patcher: self.args = args def patch(self): - project = self.file.by_type('IfcProject')[0] - storeys = self.find_decomposed_ifc_class(project, 'IfcBuildingStorey') + project = self.file.by_type("IfcProject")[0] + storeys = self.find_decomposed_ifc_class(project, "IfcBuildingStorey") for storey in storeys: co = storey.ObjectPlacement.RelativePlacement.Location.Coordinates - storey.ObjectPlacement.RelativePlacement.Location.Coordinates = (co[0], co[1], co[2]+float(self.args[0])) + storey.ObjectPlacement.RelativePlacement.Location.Coordinates = (co[0], co[1], co[2] + float(self.args[0])) co = storey.ObjectPlacement.RelativePlacement.Location.Coordinates # NOTE If the geometric data is provided (ObjectPlacement is # specified), the Elevation value shall either not be included, or diff --git a/src/ifcpatch/ifcpatch/recipes/Optimise.py b/src/ifcpatch/ifcpatch/recipes/Optimise.py index 99272ee1af..f85f585c12 100644 --- a/src/ifcpatch/ifcpatch/recipes/Optimise.py +++ b/src/ifcpatch/ifcpatch/recipes/Optimise.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -22,7 +21,6 @@ import ifcopenshell.util.element from toposort import toposort_flatten as toposort - class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -31,24 +29,22 @@ class Patcher: self.args = args self.optimized_file = ifcopenshell.file(schema=self.file.schema) - def patch(self): def generate_instances_and_references(): """ - Generator which yields an entity id and - the set of all of its references contained in its attributes. + Generator which yields an entity id and + the set of all of its references contained in its attributes. """ for inst in self.file: yield inst.id(), set(i.id() for i in self.file.traverse(inst)[1:] if i.id()) instance_mapping = {} - def map_value(v): """ - Recursive function which replicates an entity instance, with + Recursive function which replicates an entity instance, with its attributes, mapping references to already registered - instances. Indeed, because of the toposort we know that + instances. Indeed, because of the toposort we know that forward attribute value instances are mapped before the instances that reference them. """ @@ -59,15 +55,14 @@ class Patcher: if v.id() == 0: # express simple types are not part of the toposort and just copied return self.optimized_file.create_entity(v.is_a(), v[0]) - - + return instance_mapping[v] else: # a plain python value can just be returned return v info_to_id = {} - + for id in toposort(dict(generate_instances_and_references())): inst = self.file[id] info = inst.get_info(include_identifier=False, recursive=True, return_type=frozenset) @@ -76,10 +71,6 @@ class Patcher: else: info_to_id[info] = id - instance_mapping[inst] = self.optimized_file.create_entity( - inst.is_a(), - *map(map_value, inst) - ) + instance_mapping[inst] = self.optimized_file.create_entity(inst.is_a(), *map(map_value, inst)) self.file = self.optimized_file - \ No newline at end of file diff --git a/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py b/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py index ac3160837f..cba3482c5d 100644 --- a/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py +++ b/src/ifcpatch/ifcpatch/recipes/RecycleNonRootedElements.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -20,6 +19,7 @@ from collections import deque import ifcopenshell.util.element + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -31,7 +31,7 @@ class Patcher: deleted = [] hashes = {} for element in self.file: - if element.is_a('IfcRoot'): + if element.is_a("IfcRoot"): continue h = hash(tuple(element)) if h in hashes: @@ -42,13 +42,13 @@ class Patcher: hashes[h] = element deleted.sort() deleted_q = deque(deleted) - new = '' - for line in self.file.wrapped_data.to_string().split('\n'): + new = "" + for line in self.file.wrapped_data.to_string().split("\n"): try: - if int(line.split('=')[0][1:]) != deleted_q[0]: - new += (line + '\n') + if int(line.split("=")[0][1:]) != deleted_q[0]: + new += line + "\n" else: deleted_q.popleft() except: - new += (line + '\n') + new += line + "\n" self.file = new diff --git a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py index a921a08dbf..85f09bb557 100644 --- a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py +++ b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # diff --git a/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py b/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py index 6498fe135a..356673e904 100644 --- a/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py +++ b/src/ifcpatch/ifcpatch/recipes/RemoveSiteRepresentation.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -25,8 +25,8 @@ class Patcher: self.args = args def patch(self): - project = self.file.by_type('IfcProject')[0] - sites = self.find_decomposed_ifc_class(project, 'IfcSite') + project = self.file.by_type("IfcProject")[0] + sites = self.find_decomposed_ifc_class(project, "IfcSite") for site in sites: site.Representation = None diff --git a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py index 9cb10cdf65..58c5462438 100644 --- a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py +++ b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -26,8 +26,8 @@ class Patcher: def patch(self): placement_coord_ids = set() - for placement in self.file.by_type('IfcObjectPlacement'): - [placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a('IfcCartesianPoint')] + for placement in self.file.by_type("IfcObjectPlacement"): + [placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a("IfcCartesianPoint")] # Arbitrary threshold based on experience self.threshold = 1000000 @@ -42,9 +42,9 @@ class Patcher: # the case, but is very fast to run, and works for most cases. offset_point = None if self.args and len(self.args) >= 3: - offset_point = (float(self.args[0]),float(self.args[1]),float(self.args[2])) + offset_point = (float(self.args[0]), float(self.args[1]), float(self.args[2])) try: - point_lists = self.file.by_type('IfcCartesianPointList3D') + point_lists = self.file.by_type("IfcCartesianPointList3D") except: # IFC2X3 does not have IfcCartesianPointList3D point_lists = [] @@ -56,33 +56,29 @@ class Patcher: continue if not offset_point: offset_point = (-point[0], -point[1], -point[2]) - self.logger.info(f'Resetting absolute coordinates by {point}') - point = ( - point[0] + offset_point[0], - point[1] + offset_point[1], - point[2] + offset_point[2] - ) + self.logger.info(f"Resetting absolute coordinates by {point}") + point = (point[0] + offset_point[0], point[1] + offset_point[1], point[2] + offset_point[2]) coord_list[i] = point point_list.CoordList = coord_list - for point in self.file.by_type('IfcCartesianPoint'): + for point in self.file.by_type("IfcCartesianPoint"): if len(point.Coordinates) == 2 or not self.is_point_far_away(point): continue if point.id() in placement_coord_ids: continue if not offset_point: offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2]) - self.logger.info(f'Resetting absolute coordinates by {point}') + self.logger.info(f"Resetting absolute coordinates by {point}") point.Coordinates = ( point.Coordinates[0] + offset_point[0], point.Coordinates[1] + offset_point[1], - point.Coordinates[2] + offset_point[2] + point.Coordinates[2] + offset_point[2], ) def is_point_far_away(self, point): - if hasattr(point, 'Coordinates'): - return abs(point.Coordinates[0]) > self.threshold \ - or abs(point.Coordinates[1]) > self.threshold \ + if hasattr(point, "Coordinates"): + return ( + abs(point.Coordinates[0]) > self.threshold + or abs(point.Coordinates[1]) > self.threshold or abs(point.Coordinates[2]) > self.threshold - return abs(point[0]) > self.threshold \ - or abs(point[1]) > self.threshold \ - or abs(point[2]) > self.threshold + ) + return abs(point[0]) > self.threshold or abs(point[1]) > self.threshold or abs(point[2]) > self.threshold diff --git a/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py b/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py index 2747e785c7..680a9fa3f6 100644 --- a/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py +++ b/src/ifcpatch/ifcpatch/recipes/ResetSpatialElementLocations.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -25,7 +25,7 @@ class Patcher: self.args = args def patch(self): - project = self.file.by_type('IfcProject')[0] + project = self.file.by_type("IfcProject")[0] spatial_elements = self.find_decomposed_ifc_class(project, self.args[0]) for spatial_element in spatial_elements: self.patch_placement_to_origin(spatial_element) @@ -43,8 +43,8 @@ class Patcher: return results def patch_placement_to_origin(self, element): - element.ObjectPlacement.RelativePlacement.Location.Coordinates = (0., 0., 0.) + element.ObjectPlacement.RelativePlacement.Location.Coordinates = (0.0, 0.0, 0.0) if element.ObjectPlacement.RelativePlacement.Axis: - element.ObjectPlacement.RelativePlacement.Axis.DirectionRatios = (0., 0., 1.) + element.ObjectPlacement.RelativePlacement.Axis.DirectionRatios = (0.0, 0.0, 1.0) if element.ObjectPlacement.RelativePlacement.RefDirection: - element.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1., 0., 0.) + element.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1.0, 0.0, 0.0) diff --git a/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py b/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py index 0f330b64f7..a7b555279e 100644 --- a/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py +++ b/src/ifcpatch/ifcpatch/recipes/SetRefElevation.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -25,8 +25,8 @@ class Patcher: self.args = args def patch(self): - project = self.file.by_type('IfcProject')[0] - sites = self.find_decomposed_ifc_class(project, 'IfcSite') + project = self.file.by_type("IfcProject")[0] + sites = self.find_decomposed_ifc_class(project, "IfcSite") for site in sites: site.RefElevation = float(self.args[0]) diff --git a/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py b/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py index f4346fa806..5b6af064be 100644 --- a/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py +++ b/src/ifcpatch/ifcpatch/recipes/SplitByBuildingStorey.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -17,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . + class Patcher: def __init__(self, src, file, logger, args=None): self.src = src @@ -27,35 +27,39 @@ class Patcher: def patch(self): import ifcopenshell from shutil import copyfile - storeys = self.file.by_type('IfcBuildingStorey') + + storeys = self.file.by_type("IfcBuildingStorey") for i, storey in enumerate(storeys): - dest = '{}-{}.ifc'.format(i, storey.Name) + dest = "{}-{}.ifc".format(i, storey.Name) copyfile(self.src, dest) old_ifc = ifcopenshell.open(dest) new_ifc = ifcopenshell.file(schema=self.file.schema) - if self.file.schema == 'IFC2X3': - elements = old_ifc.by_type('IfcProject') + old_ifc.by_type('IfcProduct') + if self.file.schema == "IFC2X3": + elements = old_ifc.by_type("IfcProject") + old_ifc.by_type("IfcProduct") else: - elements = old_ifc.by_type('IfcContext') + old_ifc.by_type('IfcProduct') + elements = old_ifc.by_type("IfcContext") + old_ifc.by_type("IfcProduct") inverse_elements = [] for element in elements: - if element.is_a('IfcElement') \ - and not self.is_in_storey(element, storey): + if element.is_a("IfcElement") and not self.is_in_storey(element, storey): element.Representation = None continue - if element.is_a('IfcElement'): - styled_rep_items = [i for i in old_ifc.traverse(element) if i.is_a('IfcRepresentationItem') and i.StyledByItem] + if element.is_a("IfcElement"): + styled_rep_items = [ + i for i in old_ifc.traverse(element) if i.is_a("IfcRepresentationItem") and i.StyledByItem + ] [new_ifc.add(i.StyledByItem[0]) for i in styled_rep_items] new_ifc.add(element) inverse_elements.extend(old_ifc.get_inverse(element)) for inverse_element in inverse_elements: new_ifc.add(inverse_element) - for element in new_ifc.by_type('IfcElement'): + for element in new_ifc.by_type("IfcElement"): if not self.is_in_storey(element, storey): new_ifc.remove(element) new_ifc.write(dest) def is_in_storey(self, element, storey): - return element.ContainedInStructure \ - and element.ContainedInStructure[0].RelatingStructure.is_a('IfcBuildingStorey') \ + return ( + element.ContainedInStructure + and element.ContainedInStructure[0].RelatingStructure.is_a("IfcBuildingStorey") and element.ContainedInStructure[0].RelatingStructure.GlobalId == storey.GlobalId + ) diff --git a/src/ifcpatch/ifcpatch/recipes/__init__.py b/src/ifcpatch/ifcpatch/recipes/__init__.py index e38f65e07e..f703e25969 100644 --- a/src/ifcpatch/ifcpatch/recipes/__init__.py +++ b/src/ifcpatch/ifcpatch/recipes/__init__.py @@ -1,4 +1,3 @@ - # IfcPatch - IFC patching utiliy # Copyright (C) 2020, 2021 Dion Moult # @@ -16,4 +15,3 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcPatch. If not, see . - diff --git a/src/ifcpatch/make.py b/src/ifcpatch/make.py index 21de52de8e..de689038df 100644 --- a/src/ifcpatch/make.py +++ b/src/ifcpatch/make.py @@ -23,4 +23,3 @@ import subprocess cmd = f'pyinstaller ./bootstrap.py --name ifcpatch --onefile --clean --add-data "ifcpatch{os.pathsep}ifcpatch"' subprocess.check_output(cmd, shell=True) -